From 2a6fdd74b818f19ce2da563ba8a2a1ee612b2a69 Mon Sep 17 00:00:00 2001 From: Matthias Vill Date: Wed, 24 Jun 2026 21:51:44 +0200 Subject: [PATCH] Nullable Reference Types --- source/Handlebars.Benchmark/Compilation.cs | 8 +- source/Handlebars.Benchmark/CompileMany.cs | 2 +- source/Handlebars.Benchmark/EndToEnd.cs | 16 +- .../Handlebars.Benchmark.csproj | 1 + source/Handlebars.Benchmark/LargeArray.cs | 4 +- source/Handlebars.Benchmark/RenderList.cs | 6 +- source/Handlebars.Benchmark/RenderNested.cs | 16 +- source/Handlebars.Benchmark/RenderSimple.cs | 6 +- .../Handlebars.Test/BasicIntegrationTests.cs | 35 +-- source/Handlebars.Test/ClosureBuilderTests.cs | 12 +- .../Collections/FixedSizeDictionaryTests.cs | 2 +- .../ComplexIntegrationTests.cs | 2 +- .../CustomConfigurationTests.cs | 10 +- source/Handlebars.Test/DynamicTests.cs | 14 +- .../Handlebars.Test/EncodedTextWriterTests.cs | 6 +- source/Handlebars.Test/Handlebars.Test.csproj | 7 +- .../HandlebarsSpecCoverageTests.cs | 14 +- source/Handlebars.Test/HelperTests.cs | 28 +-- .../Handlebars.Test/HtmlEncoderLegacyTests.cs | 6 +- source/Handlebars.Test/HtmlEncoderTests.cs | 6 +- source/Handlebars.Test/IssueTests.cs | 14 +- .../Handlebars.Test/Issues/Issue285Tests.cs | 2 +- .../Handlebars.Test/Issues/Issue595Tests.cs | 6 +- source/Handlebars.Test/IteratorTests.cs | 4 +- source/Handlebars.Test/NumericLiteralTests.cs | 4 +- .../Handlebars.Test/PartialResolverTests.cs | 2 +- source/Handlebars.Test/PartialTests.cs | 6 +- source/Handlebars.Test/PathInfoTests.cs | 6 +- source/Handlebars.Test/RawHelperTests.cs | 20 +- source/Handlebars.Test/SubExpressionTests.cs | 6 +- .../ViewEngine/ViewEngineTests.cs | 4 +- source/Handlebars/Arguments.cs | 75 +++---- source/Handlebars/BindingContext.cs | 35 +-- source/Handlebars/BlockHelperOptions.cs | 10 +- source/Handlebars/Collections/CascadeIndex.cs | 15 +- .../Handlebars/Collections/DictionarySlim.cs | 5 +- .../Collections/ExtendedEnumerator.cs | 12 +- .../Collections/FixedSizeDictionary.cs | 19 +- source/Handlebars/Collections/IIndexed.cs | 3 + .../Collections/IObservableEvent.cs | 6 + .../Collections/IReadOnlyIndexed.cs | 5 +- .../Handlebars/Collections/ImmutableStack.cs | 16 +- source/Handlebars/Collections/LookupSlim.cs | 4 +- .../Handlebars/Collections/ObservableEvent.cs | 2 +- .../Handlebars/Collections/ObservableIndex.cs | 27 +-- .../Handlebars/Collections/ObservableList.cs | 18 +- .../Handlebars/Collections/ObserverBuilder.cs | 4 +- .../Handlebars/Collections/WeakCollection.cs | 8 +- source/Handlebars/Compiler/ClosureBuilder.cs | 204 ++++++++---------- source/Handlebars/Compiler/FunctionBuilder.cs | 2 +- .../Handlebars/Compiler/HandlebarsCompiler.cs | 2 +- .../Compiler/HandlebarsCompilerException.cs | 4 +- .../Lexer/Converter/BlockAccumulator.cs | 2 +- .../BlockAccumulatorContext.cs | 15 +- .../BlockHelperAccumulatorContext.cs | 59 ++--- .../ConditionalBlockAccumulatorContext.cs | 25 ++- .../IteratorBlockAccumulatorContext.cs | 9 +- .../PartialBlockAccumulatorContext.cs | 19 +- .../Lexer/Converter/BlockParamsConverter.cs | 5 +- .../Converter/HelperArgumentAccumulator.cs | 10 +- .../Lexer/Converter/HelperConverter.cs | 4 +- .../Lexer/Converter/RawHelperAccumulator.cs | 8 +- .../Lexer/Converter/WhitespaceRemover.cs | 4 +- .../Lexer/HandlebarsParserException.cs | 4 +- .../Lexer/Parsers/BlockParamsParser.cs | 6 +- .../Compiler/Lexer/Parsers/BlockWordParser.cs | 2 +- .../Compiler/Lexer/Parsers/CommentParser.cs | 4 +- .../Compiler/Lexer/Parsers/LiteralParser.cs | 2 +- .../Compiler/Lexer/Parsers/Parser.cs | 2 +- .../Compiler/Lexer/Parsers/PartialParser.cs | 4 +- .../Compiler/Lexer/Parsers/WordParser.cs | 2 +- .../Lexer/Tokens/BlockParameterToken.cs | 4 +- .../Lexer/Tokens/EndExpressionToken.cs | 4 +- .../Lexer/Tokens/LiteralExpressionToken.cs | 11 +- .../Compiler/Lexer/Tokens/PartialToken.cs | 4 +- .../Lexer/Tokens/StartExpressionToken.cs | 4 +- .../Compiler/Lexer/Tokens/StaticToken.cs | 6 +- .../Handlebars/Compiler/Lexer/Tokens/Token.cs | 12 +- .../Lexer/Tokens/WordExpressionToken.cs | 4 +- .../Structure/BlockParamsExpression.cs | 21 +- .../Compiler/Structure/CommentExpression.cs | 4 +- .../Structure/HandlebarsExpression.cs | 10 +- .../Compiler/Structure/HelperExpression.cs | 6 +- .../Compiler/Structure/PartialExpression.cs | 8 +- .../Expression/BlockHelperFunctionBinder.cs | 4 +- .../Expression/DecoratorDefinition.cs | 2 +- .../Expression/FunctionBinderHelpers.cs | 2 +- .../Expression/HandlebarsExpressionVisitor.cs | 11 +- .../Expression/HashParameterBinder.cs | 14 +- .../Expression/HashParameterDictionary.cs | 4 +- .../Expression/HelperFunctionBinder.cs | 4 +- .../Translation/Expression/PartialBinder.cs | 22 +- .../Translation/Expression/PathBinder.cs | 15 +- .../Expression/SubExpressionVisitor.cs | 2 +- .../Configuration/CompileTimeConfiguration.cs | 2 +- .../Configuration/HandlebarsConfiguration.cs | 10 +- .../HandlebarsConfigurationAdapter.cs | 12 +- .../ICompiledHandlebarsConfiguration.cs | 12 +- source/Handlebars/Context.cs | 17 +- .../Decorators/BlockDecoratorOptions.cs | 6 +- source/Handlebars/DynamicViewModel.cs | 3 +- ...ainSegment.ChainSegmentEqualityComparer.cs | 2 +- .../PathInfo.TrimmedPathEqualityComparer.cs | 4 +- .../ReferenceEqualityComparer.cs | 2 +- .../StringEqualityComparer.cs | 2 +- .../Extensions/EnumerableExtensions.cs | 13 +- .../Handlebars/Extensions/TypeExtensions.cs | 3 +- .../Features/MissingHelperFeature.cs | 26 +-- source/Handlebars/Features/WarmUpFeature.cs | 2 +- .../FileSystemPartialTemplateResolver.cs | 2 +- source/Handlebars/Handlebars.cs | 4 +- source/Handlebars/Handlebars.csproj | 1 + source/Handlebars/HandlebarsEnvironment.cs | 12 +- source/Handlebars/HandlebarsException.cs | 6 +- source/Handlebars/HandlebarsExtensions.cs | 4 +- .../Handlebars/HandlebarsRuntimeException.cs | 4 +- source/Handlebars/HandlebarsUtils.cs | 11 +- source/Handlebars/HandlebarsViewEngine.cs | 6 +- .../DelegateReturnBlockHelperDescriptor.cs | 2 +- .../LateBindBlockHelperDescriptor.cs | 2 +- .../MissingBlockHelperDescriptor.cs | 2 +- .../BlockHelpers/WithBlockHelperDescriptor.cs | 2 +- .../Helpers/DelegateReturnHelperDescriptor.cs | 2 +- ...legateReturnHelperWithOptionsDescriptor.cs | 2 +- source/Handlebars/Helpers/HelperExtensions.cs | 2 +- .../Handlebars/Helpers/IHelperDescriptor.cs | 2 +- source/Handlebars/Helpers/IHelperResolver.cs | 5 +- .../Helpers/LateBindHelperDescriptor.cs | 8 +- .../Helpers/LookupReturnHelperDescriptor.cs | 4 +- source/Handlebars/IHandlebars.cs | 8 +- source/Handlebars/IHelperOptions.cs | 5 +- source/Handlebars/IO/EncodedTextWriter.cs | 28 +-- .../Handlebars/IO/EncodedTextWriterWrapper.cs | 4 +- .../Formatters/CollectionFormatterProvider.cs | 23 +- .../IO/Formatters/DefaultFormatterProvider.cs | 3 +- .../DefaultFormatters/DefaultBoolFormatter.cs | 3 +- .../DefaultFormatters/DefaultCharFormatter.cs | 3 +- .../DefaultDateTimeFormatter.cs | 3 +- .../DefaultDecimalFormatter.cs | 3 +- .../DefaultDoubleFormatter.cs | 3 +- .../DefaultFloatFormatter.cs | 3 +- .../DefaultFormatters/DefaultIntFormatter.cs | 3 +- .../DefaultFormatters/DefaultLongFormatter.cs | 3 +- .../DefaultObjectFormatter.cs | 6 +- .../DefaultShortFormatter.cs | 3 +- .../DefaultFormatters/DefaultUIntFormatter.cs | 3 +- .../DefaultULongFormatter.cs | 3 +- .../DefaultUShortFormatter.cs | 3 +- .../IO/Formatters/FormatterProvider.cs | 29 +-- .../IO/Formatters/IFormatterProvider.cs | 3 +- .../ReadOnlyCollectionFormatterProvider.cs | 23 +- .../IO/Formatters/UndefinedFormatter.cs | 9 +- source/Handlebars/IO/HtmlEncoder.cs | 8 +- source/Handlebars/IO/HtmlEncoderLegacy.cs | 8 +- source/Handlebars/IO/ITextEncoder.cs | 6 +- source/Handlebars/IO/PolledStringWriter.cs | 6 +- source/Handlebars/IO/TextEncoderWrapper.cs | 12 +- source/Handlebars/IPartialTemplateResolver.cs | 2 +- source/Handlebars/Iterators/ArrayIterator.cs | 2 +- .../Iterators/CollectionIterator'2.cs | 4 +- .../Iterators/CollectionIterator.cs | 5 +- .../Iterators/DictionaryIterator'2.cs | 3 +- .../Iterators/EnumerableIterator'2.cs | 2 +- source/Handlebars/Iterators/ListIterator'2.cs | 2 +- .../Iterators/ReadOnlyCollectionIterator'2.cs | 2 +- .../Iterators/ReadOnlyDictionaryIterator'2.cs | 3 +- .../Iterators/ReadOnlyListIterator'2.cs | 2 +- source/Handlebars/LayoutViewModel.cs | 17 +- .../GenericDictionaryAccessor.cs | 4 +- .../ReadOnlyGenericDictionaryAccessor.cs | 4 +- .../ReadOnlyStringDictionaryAccessor.cs | 2 +- .../StringDictionaryAccessor.cs | 2 +- .../DictionaryMemberAccessor.cs | 2 +- .../MemberAccessors/DynamicMemberAccessor.cs | 5 +- .../EnumerableMemberAccessor'2.cs | 2 +- .../EnumerableMemberAccessor.cs | 10 +- .../EnumerableAccessors/ListMemberAccessor.cs | 2 +- .../ReadOnlyListMemberAccessor.cs | 3 +- .../MemberAccessors/IMemberAccessor.cs | 3 +- .../MemberAccessors/MergedMemberAccessor.cs | 2 +- .../ReflectionMemberAccessor.cs | 37 ++-- .../CollectionMemberAliasProvider.cs | 4 +- .../DelegatedMemberAliasProvider.cs | 2 +- .../IMemberAliasProvider.cs | 2 +- .../DictionaryObjectDescriptor.cs | 10 +- .../DynamicObjectDescriptor.cs | 3 +- .../EnumerableObjectDescriptor.cs | 64 +++--- ...nericDictionaryObjectDescriptorProvider.cs | 17 +- .../IObjectDescriptorProvider.cs | 3 +- .../ObjectDescriptors/ObjectAccessor.cs | 23 +- .../ObjectDescriptors/ObjectDescriptor.cs | 27 ++- .../ObjectDescriptorFactory.cs | 15 +- .../ObjectDescriptorProvider.cs | 14 +- ...nericDictionaryObjectDescriptorProvider.cs | 17 +- ...tringDictionaryObjectDescriptorProvider.cs | 16 +- ...tringDictionaryObjectDescriptorProvider.cs | 16 +- source/Handlebars/PathInfoLight.cs | 4 +- .../Handlebars/PathStructure/ChainSegment.cs | 10 +- .../PathStructure/ChainSegmentStore.cs | 4 +- source/Handlebars/PathStructure/PathInfo.cs | 36 +++- .../Handlebars/PathStructure/PathInfoStore.cs | 7 +- .../Handlebars/PathStructure/PathResolver.cs | 16 +- .../Handlebars/PathStructure/PathSegment.cs | 4 +- source/Handlebars/Polyfills/AsyncLocal.cs | 2 + .../Polyfills/NullableAttributes.cs | 144 +++++++++++++ .../Handlebars/Pools/BindingContext.Pool.cs | 14 +- source/Handlebars/Runtime/AmbientContext.cs | 44 ++-- source/Handlebars/Runtime/DeferredValue.cs | 11 +- source/Handlebars/Runtime/GcDeferredValue.cs | 12 +- source/Handlebars/Runtime/Ref.cs | 4 +- source/Handlebars/StringUtils/Substring.cs | 17 +- source/Handlebars/UndefinedBindingResult.cs | 6 +- .../ValueProviders/BlockParamsValues.cs | 12 +- .../Handlebars/ValueProviders/DataValues.cs | 10 +- .../ValueProviders/IteratorValues.cs | 12 +- .../ValueProviders/ObjectIteratorValues.cs | 13 +- source/Handlebars/_Delegates.cs | 6 +- 217 files changed, 1274 insertions(+), 1000 deletions(-) create mode 100644 source/Handlebars/Collections/IObservableEvent.cs create mode 100644 source/Handlebars/Polyfills/NullableAttributes.cs diff --git a/source/Handlebars.Benchmark/Compilation.cs b/source/Handlebars.Benchmark/Compilation.cs index e04988d8..7c6230f1 100644 --- a/source/Handlebars.Benchmark/Compilation.cs +++ b/source/Handlebars.Benchmark/Compilation.cs @@ -6,7 +6,7 @@ namespace HandlebarsNet.Benchmark { public class Compilation { - private IHandlebars _handlebars; + private IHandlebars _handlebars = null!; [GlobalSetup] public void Setup() @@ -14,9 +14,9 @@ public void Setup() _handlebars = Handlebars.Create(); using(_handlebars.Configure()) { - _handlebars.RegisterHelper("pow1", (output, context, arguments) => output.WriteSafeString(((int)arguments[0] * (int) arguments[0]).ToString())); - _handlebars.RegisterHelper("pow2", (output, context, arguments) => output.WriteSafeString(((int)arguments[0] * (int) arguments[0]).ToString())); - _handlebars.RegisterHelper("pow5", (output, options, context, arguments) => output.WriteSafeString(((int)arguments[0] * (int) arguments[0]).ToString())); + _handlebars.RegisterHelper("pow1", (output, context, arguments) => output.WriteSafeString(((int)arguments[0]! * (int) arguments[0]!).ToString())); + _handlebars.RegisterHelper("pow2", (output, context, arguments) => output.WriteSafeString(((int)arguments[0]! * (int) arguments[0]!).ToString())); + _handlebars.RegisterHelper("pow5", (output, options, context, arguments) => output.WriteSafeString(((int)arguments[0]! * (int) arguments[0]!).ToString())); } } diff --git a/source/Handlebars.Benchmark/CompileMany.cs b/source/Handlebars.Benchmark/CompileMany.cs index 86f089d5..465b1d8b 100644 --- a/source/Handlebars.Benchmark/CompileMany.cs +++ b/source/Handlebars.Benchmark/CompileMany.cs @@ -19,7 +19,7 @@ namespace HandlebarsNet.Benchmark [MemoryDiagnoser] public class CompileMany { - private IHandlebars _env; + private IHandlebars _env = null!; // -> Setup() // A realistic multi-expression template; keeps compilation non-trivial. private const string Template = diff --git a/source/Handlebars.Benchmark/EndToEnd.cs b/source/Handlebars.Benchmark/EndToEnd.cs index 2c65981c..379ab152 100644 --- a/source/Handlebars.Benchmark/EndToEnd.cs +++ b/source/Handlebars.Benchmark/EndToEnd.cs @@ -10,14 +10,14 @@ namespace HandlebarsNet.Benchmark { public class EndToEnd { - private object _data; - private HandlebarsTemplate _default; + private object? _data; + private HandlebarsTemplate _default = null!; // -> Setup() [Params(5)] public int N { get; set; } - + [Params("object", "dictionary")] - public string DataType { get; set; } + public string DataType { get; set; } = null!; [GlobalSetup] public void Setup() @@ -181,12 +181,12 @@ private class PowHelper : IHelperDescriptor public object Invoke(in HelperOptions options, in Context context, in Arguments arguments) { - return ((int)arguments[0] * (int)arguments[0]).ToString(); + return ((int)arguments[0]! * (int)arguments[0]!).ToString(); } public void Invoke(in EncodedTextWriter output, in HelperOptions options, in Context context, in Arguments arguments) { - output.WriteSafeString(((int)arguments[0] * (int)arguments[0]).ToString()); + output.WriteSafeString(((int)arguments[0]! * (int)arguments[0]!).ToString()); } } @@ -198,12 +198,12 @@ private class BlockPowHelper : IHelperDescriptor public object Invoke(in BlockHelperOptions options, in Context context, in Arguments arguments) { - return ((int)arguments[0] * (int)arguments[0]).ToString(); + return ((int)arguments[0]! * (int)arguments[0]!).ToString(); } public void Invoke(in EncodedTextWriter output, in BlockHelperOptions options, in Context context, in Arguments arguments) { - output.WriteSafeString(((int)arguments[0] * (int)arguments[0]).ToString()); + output.WriteSafeString(((int)arguments[0]! * (int)arguments[0]!).ToString()); } } diff --git a/source/Handlebars.Benchmark/Handlebars.Benchmark.csproj b/source/Handlebars.Benchmark/Handlebars.Benchmark.csproj index 74794c00..1c7461bf 100644 --- a/source/Handlebars.Benchmark/Handlebars.Benchmark.csproj +++ b/source/Handlebars.Benchmark/Handlebars.Benchmark.csproj @@ -6,6 +6,7 @@ false false HandlebarsNet.Benchmark + enable diff --git a/source/Handlebars.Benchmark/LargeArray.cs b/source/Handlebars.Benchmark/LargeArray.cs index 6a989981..7b367e34 100644 --- a/source/Handlebars.Benchmark/LargeArray.cs +++ b/source/Handlebars.Benchmark/LargeArray.cs @@ -9,8 +9,8 @@ namespace HandlebarsNet.Benchmark { public class LargeArray { - private object _data; - private HandlebarsTemplate _default; + private object _data = null!; // -> Setup() + private HandlebarsTemplate _default = null!; // -> Setup() [Params(20000, 40000, 80000)] public int N { get; set; } diff --git a/source/Handlebars.Benchmark/RenderList.cs b/source/Handlebars.Benchmark/RenderList.cs index 3968e2c1..f47a2adb 100644 --- a/source/Handlebars.Benchmark/RenderList.cs +++ b/source/Handlebars.Benchmark/RenderList.cs @@ -11,8 +11,8 @@ namespace HandlebarsNet.Benchmark [MemoryDiagnoser] public class RenderList { - private HandlebarsTemplate _template; - private object _data; + private HandlebarsTemplate _template = null!; // -> Setup() + private object _data = null!; // -> Setup() private const string Source = "{{#each items}}" + @@ -26,7 +26,7 @@ public class RenderList public int N { get; set; } [Params("object", "dictionary")] - public string DataType { get; set; } + public string DataType { get; set; } = null!; [GlobalSetup] public void Setup() diff --git a/source/Handlebars.Benchmark/RenderNested.cs b/source/Handlebars.Benchmark/RenderNested.cs index 5f8737e3..c1a68f8a 100644 --- a/source/Handlebars.Benchmark/RenderNested.cs +++ b/source/Handlebars.Benchmark/RenderNested.cs @@ -11,8 +11,8 @@ namespace HandlebarsNet.Benchmark [MemoryDiagnoser] public class RenderNested { - private HandlebarsTemplate _template; - private object _data; + private HandlebarsTemplate _template = null!; // -> Setup() + private object _data = null!; // -> Setup() private const int SectionCount = 5; @@ -32,7 +32,7 @@ public class RenderNested public int RowsPerSection { get; set; } [Params("object", "dictionary")] - public string DataType { get; set; } + public string DataType { get; set; } = null!; [GlobalSetup] public void Setup() @@ -53,7 +53,7 @@ public void Setup() private static readonly string[] SectionNames = ["Traffic", "Revenue", "Errors", "Performance", "Conversion"]; private static readonly string[] MetricLabels = ["Count", "Rate", "p50", "p99", "Total", "Delta", "Avg", "Max", "Min", "StdDev", "Score", "Index", "Ratio", "Share", "Volume", "Trend", "Baseline", "Peak", "Trough", "Median"]; - private static readonly string[] Units = ["req/s", "%", "ms", "$", null, null]; // nulls make ~half unitless + private static readonly string?[] Units = ["req/s", "%", "ms", "$", null, null]; // nulls make ~half unitless private object BuildObjectData() { @@ -66,7 +66,7 @@ private object BuildObjectData() { bool withinTarget = (s * RowsPerSection + r) % 4 != 0; if (!withinTarget) alertCount++; - string unit = Units[(s * RowsPerSection + r) % Units.Length]; + string? unit = Units[(s * RowsPerSection + r) % Units.Length]; metrics.Add(new { label = MetricLabels[(s * RowsPerSection + r) % MetricLabels.Length], @@ -94,13 +94,13 @@ private Dictionary BuildDictionaryData() var sections = new List>(SectionCount); for (int s = 0; s < SectionCount; s++) { - var metrics = new List>(RowsPerSection); + var metrics = new List>(RowsPerSection); for (int r = 0; r < RowsPerSection; r++) { bool withinTarget = (s * RowsPerSection + r) % 4 != 0; if (!withinTarget) alertCount++; - string unit = Units[(s * RowsPerSection + r) % Units.Length]; - metrics.Add(new Dictionary + string? unit = Units[(s * RowsPerSection + r) % Units.Length]; + metrics.Add(new Dictionary { ["label"] = MetricLabels[(s * RowsPerSection + r) % MetricLabels.Length], ["value"] = $"{42.0 + s * 10 + r:F1}", diff --git a/source/Handlebars.Benchmark/RenderSimple.cs b/source/Handlebars.Benchmark/RenderSimple.cs index f16e172f..5c428288 100644 --- a/source/Handlebars.Benchmark/RenderSimple.cs +++ b/source/Handlebars.Benchmark/RenderSimple.cs @@ -11,8 +11,8 @@ namespace HandlebarsNet.Benchmark [MemoryDiagnoser] public class RenderSimple { - private HandlebarsTemplate _template; - private object _data; + private HandlebarsTemplate _template = null!; // -> Setup() + private object _data = null!; // -> Setup() private const string Source = "Dear {{user.firstName}} {{user.lastName}},\n\n" + @@ -23,7 +23,7 @@ public class RenderSimple "— The {{store.name}} Team"; [Params("object", "dictionary", "expando")] - public string DataType { get; set; } + public string DataType { get; set; } = null!; [GlobalSetup] public void Setup() diff --git a/source/Handlebars.Test/BasicIntegrationTests.cs b/source/Handlebars.Test/BasicIntegrationTests.cs index bb2882c2..3f655750 100644 --- a/source/Handlebars.Test/BasicIntegrationTests.cs +++ b/source/Handlebars.Test/BasicIntegrationTests.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq; using System.Reflection; @@ -22,7 +23,7 @@ public class BasicIntegrationTests private static string HtmlEncodeStringHelper(IHandlebars handlebars, string inputString) { using var stringWriter = new System.IO.StringWriter(); - handlebars.Configuration.TextEncoder.Encode(inputString, stringWriter); + handlebars.Configuration.TextEncoder!.Encode(inputString, stringWriter); return stringWriter.ToString(); } @@ -208,7 +209,7 @@ public void BasicPathThrowOnNestedUnresolvedBindingExpression(IHandlebars handle var data = new { - foo = (object)null + foo = (object?)null }; var ex = Assert.Throws(() => template(data)); @@ -230,7 +231,7 @@ public void BasicPathNoThrowOnNullExpression(IHandlebars handlebars) var data = new { - foo = (string)null + foo = (string?)null }; var result = template(data); Assert.Contains("false", result); @@ -1280,7 +1281,7 @@ public void BasicDeferredBlockNull(IHandlebars handlebars) var data = new { - person = (object)null + person = (object?)null }; var result = template(data); @@ -1384,7 +1385,7 @@ public void BasicNullOrMissingSubProperty(IHandlebars handlebars) var data = new { - name = (object)null + name = (object?)null }; var result = template(data); @@ -1416,7 +1417,7 @@ public void BasicNullFalsy(IHandlebars handlebars) var data = new { - falsy = (object)null + falsy = (object?)null }; var result = template(data); @@ -1978,10 +1979,10 @@ public void CollectionReturnFromHelper(IHandlebars handlebars) { handlebars.RegisterHelper($"getData", (context, arguments) => { - var data = new Dictionary + var data = new Dictionary { - {"Nils", arguments[0].ToString()}, - {"Yehuda", arguments[1].ToString()} + {"Nils", arguments[0]!.ToString()}, + {"Yehuda", arguments[1]!.ToString()} }; return data; @@ -2209,7 +2210,7 @@ public void ChainedPathIteratorHelper() private class StringHelperResolver : IHelperResolver { - public bool TryResolveHelper(PathInfo name, Type targetType, out IHelperDescriptor helper) + public bool TryResolveHelper(PathInfo name, Type? targetType, out IHelperDescriptor helper) { if (targetType == typeof(string)) { @@ -2218,7 +2219,7 @@ public bool TryResolveHelper(PathInfo name, Type targetType, out IHelperDescript if (method == null) { - helper = null; + helper = null!; return false; } @@ -2226,13 +2227,13 @@ public bool TryResolveHelper(PathInfo name, Type targetType, out IHelperDescript return true; } - helper = null; + helper = null!; return false; } public bool TryResolveBlockHelper(PathInfo name, out IHelperDescriptor helper) { - helper = null; + helper = null!; return false; } @@ -2249,7 +2250,7 @@ public HelperDescriptor(PathInfo name, MethodInfo methodInfo) public PathInfo Name { get; } public object Invoke(in HelperOptions options, in Context context, in Arguments arguments) { - return _methodInfo.Invoke(arguments[0], arguments.AsEnumerable().Skip(1).ToArray()); + return _methodInfo.Invoke(arguments[0], arguments.AsEnumerable().Skip(1).ToArray())!; } public void Invoke(in EncodedTextWriter output, in HelperOptions options, in Context context, in Arguments arguments) @@ -2270,7 +2271,7 @@ public bool TryCreateFormatter(Type type, out IFormatter formatter) { if (type != typeof(UndefinedBindingResult)) { - formatter = null; + formatter = null!; return false; } @@ -2297,7 +2298,7 @@ public bool TryCreateFormatter(Type type, out IFormatter formatter) { if (type != typeof(DateTime)) { - formatter = null; + formatter = null!; return false; } @@ -2458,7 +2459,7 @@ public bool Remove(KeyValuePair item) public bool TryGetValue(string key, out object value) { - return ((IDictionary)data).TryGetValue(key, out value); + return ((IDictionary)data).TryGetValue(key, out value!); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() diff --git a/source/Handlebars.Test/ClosureBuilderTests.cs b/source/Handlebars.Test/ClosureBuilderTests.cs index 419fefdd..08dc32cd 100644 --- a/source/Handlebars.Test/ClosureBuilderTests.cs +++ b/source/Handlebars.Test/ClosureBuilderTests.cs @@ -25,19 +25,19 @@ public void GeneratesClosureWithOverflow() Assert.Equal(paths[0], closure.PI0); Assert.Equal(paths[3], closure.PI3); - Assert.Equal(paths[5], closure.PIA[1]); + Assert.Equal(paths[5], closure.PIA![1]); Assert.Equal(helpers[0], closure.HD0); Assert.Equal(helpers[3], closure.HD3); - Assert.Equal(helpers[5], closure.HDA[1]); + Assert.Equal(helpers[5], closure.HDA![1]); Assert.Equal(blockHelpers[0], closure.BHD0); Assert.Equal(blockHelpers[3], closure.BHD3); - Assert.Equal(blockHelpers[5], closure.BHDA[1]); + Assert.Equal(blockHelpers[5], closure.BHDA![1]); Assert.Equal(decoratorDelegates[0], closure.DDD0); Assert.Equal(decoratorDelegates[3], closure.DDD3); - Assert.Equal(decoratorDelegates[5], closure.DDDA[1]); + Assert.Equal(decoratorDelegates[5], closure.DDDA![1]); Assert.Equal(others[0], closure.A[0]); Assert.Equal(others[3], closure.A[3]); @@ -97,7 +97,7 @@ private static List>> GenerateBlockHel var blockHelpers = new List>>(); for (int i = 0; i < count; i++) { - var blockHelper = new Ref>(); + var blockHelper = new Ref>(null!); builder.Add(Const(blockHelper)); blockHelpers.Add(blockHelper); } @@ -110,7 +110,7 @@ private static List>> GenerateHelpers(Closu var helpers = new List>>(); for (int i = 0; i < count; i++) { - var helper = new Ref>(); + var helper = new Ref>(null!); builder.Add(Const(helper)); helpers.Add(helper); } diff --git a/source/Handlebars.Test/Collections/FixedSizeDictionaryTests.cs b/source/Handlebars.Test/Collections/FixedSizeDictionaryTests.cs index af7c44b0..3c0b2cb3 100644 --- a/source/Handlebars.Test/Collections/FixedSizeDictionaryTests.cs +++ b/source/Handlebars.Test/Collections/FixedSizeDictionaryTests.cs @@ -35,7 +35,7 @@ public CollisionsComparer(int hash) _hash = hash; } - public new bool Equals(object x, object y) => false; + public new bool Equals(object? x, object? y) => false; public int GetHashCode(object obj) => _hash; } diff --git a/source/Handlebars.Test/ComplexIntegrationTests.cs b/source/Handlebars.Test/ComplexIntegrationTests.cs index bed68944..da1c26d0 100644 --- a/source/Handlebars.Test/ComplexIntegrationTests.cs +++ b/source/Handlebars.Test/ComplexIntegrationTests.cs @@ -132,7 +132,7 @@ public void BlockHelperHelper() }); Handlebars.RegisterHelper("block_helper", (writer, options, context, arguments) => { - foreach(var item in arguments[0] as IEnumerable) + foreach(var item in (IEnumerable)arguments[0]!) { options.Template(writer, item); } diff --git a/source/Handlebars.Test/CustomConfigurationTests.cs b/source/Handlebars.Test/CustomConfigurationTests.cs index 997d2fb3..e16610c9 100644 --- a/source/Handlebars.Test/CustomConfigurationTests.cs +++ b/source/Handlebars.Test/CustomConfigurationTests.cs @@ -67,19 +67,19 @@ public void SnakeCaseInputModelNaming() private class JsonEncoder : ITextEncoder { - public void Encode(StringBuilder text, TextWriter target) + public void Encode(StringBuilder? text, TextWriter target) { - target.Write(JsonConvert.ToString(text.ToString(), '"').Trim('"')); + target.Write(JsonConvert.ToString(text?.ToString(), '"').Trim('"')); } - public void Encode(string text, TextWriter target) + public void Encode(string? text, TextWriter target) { target.Write(JsonConvert.ToString(text, '"').Trim('"')); } - public void Encode(T text, TextWriter target) where T : IEnumerator + public void Encode(T? text, TextWriter target) where T : IEnumerator { - Encode(new string(new Adapter(text).ToArray()), target); + Encode(new string(new Adapter(text!).ToArray()), target); } public IFormatProvider FormatProvider { get; } = CultureInfo.InvariantCulture; diff --git a/source/Handlebars.Test/DynamicTests.cs b/source/Handlebars.Test/DynamicTests.cs index b11fc6d8..c2930099 100644 --- a/source/Handlebars.Test/DynamicTests.cs +++ b/source/Handlebars.Test/DynamicTests.cs @@ -3,6 +3,7 @@ using Xunit; using System.Dynamic; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -139,7 +140,7 @@ public void JsonTestObjects(IHandlebars handlebars){ [Theory] [ClassData(typeof(EnvGenerator))] public void JObjectTest(IHandlebars handlebars) { - object nullValue = null; + object? nullValue = null; JObject model = JObject.FromObject(new { Nested = new { Prop = "Prop" }, Nested2 = nullValue }); var source = "{{NotExists.Prop}}"; @@ -320,16 +321,7 @@ public override IEnumerable GetDynamicMemberNames() public override bool TryGetMember(GetMemberBinder binder, out object result) { - if(_properties.ContainsKey(binder.Name)) - { - result = _properties[binder.Name]; - return true; - } - else - { - result = null; - return false; - } + return _properties.TryGetValue(binder.Name, out result!); } } } diff --git a/source/Handlebars.Test/EncodedTextWriterTests.cs b/source/Handlebars.Test/EncodedTextWriterTests.cs index 0432ee05..ccc26b34 100644 --- a/source/Handlebars.Test/EncodedTextWriterTests.cs +++ b/source/Handlebars.Test/EncodedTextWriterTests.cs @@ -29,15 +29,15 @@ public void Write(object value) { var stringWriter = new StringWriter(); var formatterProvider = Substitute.For(); - formatterProvider.TryCreateFormatter(Arg.Any(), out Arg.Any()) + formatterProvider.TryCreateFormatter(Arg.Any(), out Arg.Any()) .Returns(o => { - new DefaultFormatterProvider().TryCreateFormatter(o[0] as Type, out var formatter); + new DefaultFormatterProvider().TryCreateFormatter((Type)o[0], out var formatter); o[1] = formatter; return true; }); - formatterProvider.TryCreateFormatter(typeof(UndefinedBindingResult), out Arg.Any()) + formatterProvider.TryCreateFormatter(typeof(UndefinedBindingResult), out Arg.Any()) .Returns(o => { o[1] = new UndefinedFormatter("{0}"); diff --git a/source/Handlebars.Test/Handlebars.Test.csproj b/source/Handlebars.Test/Handlebars.Test.csproj index 5b0126f6..fbef9126 100644 --- a/source/Handlebars.Test/Handlebars.Test.csproj +++ b/source/Handlebars.Test/Handlebars.Test.csproj @@ -9,6 +9,7 @@ false true 0618;1701 + enable @@ -29,13 +30,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - - diff --git a/source/Handlebars.Test/HandlebarsSpecCoverageTests.cs b/source/Handlebars.Test/HandlebarsSpecCoverageTests.cs index 014c03eb..dda21df6 100644 --- a/source/Handlebars.Test/HandlebarsSpecCoverageTests.cs +++ b/source/Handlebars.Test/HandlebarsSpecCoverageTests.cs @@ -318,7 +318,7 @@ public void Unless_WithElse(IHandlebars hbs) public void Unless_NullIsFalsy(IHandlebars hbs) { var template = hbs.Compile("{{#unless val}}missing{{else}}found{{/unless}}"); - Assert.Equal("missing", template(new { val = (string)null })); + Assert.Equal("missing", template(new { val = (string?)null })); } [Theory, ClassData(typeof(HandlebarsEnvGenerator))] @@ -372,7 +372,7 @@ public void Each_ObjectKeyWithHtmlCharsIsEncoded(IHandlebars hbs) public void Each_NullCollectionGoesToElse(IHandlebars hbs) { var template = hbs.Compile("{{#each items}}{{this}}{{else}}nothing{{/each}}"); - Assert.Equal("nothing", template(new { items = (string[])null })); + Assert.Equal("nothing", template(new { items = (string[]?)null })); } [Theory, ClassData(typeof(HandlebarsEnvGenerator))] @@ -432,7 +432,7 @@ public void Each_IndexCommaLastSeparatorPattern(IHandlebars hbs) public void With_NullGoesToElse(IHandlebars hbs) { var template = hbs.Compile("{{#with val}}yes{{else}}no{{/with}}"); - Assert.Equal("no", template(new { val = (string)null })); + Assert.Equal("no", template(new { val = (string?)null })); } [Theory, ClassData(typeof(HandlebarsEnvGenerator))] @@ -650,14 +650,14 @@ public void Path_HyphenatedIdentifier(IHandlebars hbs) public void Path_NullIntermediateRendersEmpty(IHandlebars hbs) { var template = hbs.Compile("{{a.b.c}}"); - Assert.Equal("", template(new { a = (object)null })); + Assert.Equal("", template(new { a = (object?)null })); } [Theory, ClassData(typeof(HandlebarsEnvGenerator))] public void Path_DeepNullIntermediateRendersEmpty(IHandlebars hbs) { var template = hbs.Compile("{{person.name}}"); - Assert.Equal("", template(new { person = (object)null })); + Assert.Equal("", template(new { person = (object?)null })); } // [SPEC GAP] Boolean false renders as "False" (capital F) instead of "false" (lowercase). @@ -689,7 +689,7 @@ public void Path_ZeroRendersAsZeroString(IHandlebars hbs) public void Path_NullRendersAsEmpty(IHandlebars hbs) { var template = hbs.Compile("{{val}}"); - Assert.Equal("", template(new { val = (string)null })); + Assert.Equal("", template(new { val = (string?)null })); } // ───────────────────────────────────────────────────────────── @@ -1015,7 +1015,7 @@ public void BlockParams_NestedScopesShadowIndependently(IHandlebars hbs) public void Truthiness_NullIsFalsy(IHandlebars hbs) { var t = hbs.Compile("{{#if v}}T{{else}}F{{/if}}"); - Assert.Equal("F", t(new { v = (object)null })); + Assert.Equal("F", t(new { v = (object?)null })); } [Theory, ClassData(typeof(HandlebarsEnvGenerator))] diff --git a/source/Handlebars.Test/HelperTests.cs b/source/Handlebars.Test/HelperTests.cs index bbd2b63e..e0be6733 100644 --- a/source/Handlebars.Test/HelperTests.cs +++ b/source/Handlebars.Test/HelperTests.cs @@ -632,27 +632,27 @@ public void BlockHelperWithArbitraryInversionAndComplexOperator() writer.Write("ifCond:Wrong number of arguments"); return; } - if (args[0] == null || args[0].GetType().Name == "UndefinedBindingResult") + if (args[0] == null || args[0]!.GetType().Name == "UndefinedBindingResult") { writer.Write("ifCond:args[0] undefined"); return; } - if (args[1] == null || args[1].GetType().Name == "UndefinedBindingResult") + if (args[1] == null || args[1]!.GetType().Name == "UndefinedBindingResult") { writer.Write("ifCond:args[1] undefined"); return; } - if (args[2] == null || args[2].GetType().Name == "UndefinedBindingResult") + if (args[2] == null || args[2]!.GetType().Name == "UndefinedBindingResult") { writer.Write("ifCond:args[2] undefined"); return; } - if (args[0].GetType().Name == "String" || args[0].GetType().Name == "JValue") + if (args[0]!.GetType().Name == "String" || args[0]!.GetType().Name == "JValue") { - var val1 = args[0].ToString(); - var val2 = args[2].ToString(); + var val1 = args[0]!.ToString()!; + var val2 = args[2]!.ToString()!; - switch (args[1].ToString()) + switch (args[1]!.ToString()) { case ">": if (val1.Length > val2.Length) @@ -700,10 +700,10 @@ public void BlockHelperWithArbitraryInversionAndComplexOperator() } else { - var val1 = float.Parse(args[0].ToString()); - var val2 = float.Parse(args[2].ToString()); + var val1 = float.Parse(args[0]!.ToString()!); + var val2 = float.Parse(args[2]!.ToString()!); - switch (args[1].ToString()) + switch (args[1]!.ToString()) { case ">": if (val1 > val2) @@ -821,7 +821,7 @@ public void HelperWithHashArgument() { var h = Handlebars.Create(); h.RegisterHelper("myHelper", (writer, context, args) => { - var hash = args[2] as Dictionary; + var hash = (Dictionary) args[2]!; foreach(var item in hash) { writer.Write(" {0}: {1}", item.Key, item.Value); @@ -844,7 +844,7 @@ public void BlockHelperWithSubExpression() { Handlebars.RegisterHelper("isEqual", (writer, context, args) => { - writer.WriteSafeString(args[0].ToString() == args[1].ToString() ? "true" : null); + writer.WriteSafeString(args[0]!.ToString() == args[1]!.ToString() ? "true" : null); }); var source = "{{#if (isEqual arg1 arg2)}}True{{/if}}"; @@ -912,7 +912,7 @@ public void HelperWithLiteralHashValues() var source = "{{literalHelper Bool=true Integer=1 String=\"abc\"}}"; Handlebars.RegisterHelper("literalHelper", (writer, context, arguments) => { - var parameters = arguments[0] as IDictionary; + var parameters = (IDictionary) arguments[0]!; Assert.IsType(parameters["Bool"]); Assert.IsType(parameters["Integer"]); Assert.IsType(parameters["String"]); @@ -1021,7 +1021,7 @@ public void Invoke(in EncodedTextWriter output, in BlockHelperOptions options, i data.CreateProperty(ChainSegment.Value, null, out var valueIndex); var iterationIndex = 0; - foreach (var item in (IEnumerable) arguments[0]) + foreach (var item in (IEnumerable) arguments[0]!) { data[ChainSegment.Index] = iterationIndex; data[valueIndex] = item; diff --git a/source/Handlebars.Test/HtmlEncoderLegacyTests.cs b/source/Handlebars.Test/HtmlEncoderLegacyTests.cs index 8c747d0a..c958b41c 100644 --- a/source/Handlebars.Test/HtmlEncoderLegacyTests.cs +++ b/source/Handlebars.Test/HtmlEncoderLegacyTests.cs @@ -46,7 +46,7 @@ public void EscapeCorrectCharactersHandlebarsNetLegacyRules(string input, string [InlineData("\"", """)] [InlineData("&a&", "&a&")] [InlineData("a&a", "a&a")] - public void EncodeTestHandlebarsNetLegacyRules(string input, string expected) + public void EncodeTestHandlebarsNetLegacyRules(string? input, string expected) { // Arrange using var writer = new StringWriter(); @@ -63,7 +63,7 @@ public void EncodeTestHandlebarsNetLegacyRules(string input, string expected) [InlineData("", "")] [InlineData("a", "a")] [InlineData("<", "<")] - public void EncodeStringBuilderOverload(string input, string expected) + public void EncodeStringBuilderOverload(string? input, string expected) { using var writer = new StringWriter(); @@ -78,7 +78,7 @@ public void EncodeStringBuilderOverload(string input, string expected) [InlineData(null, "")] [InlineData("a", "a")] [InlineData("<", "<")] - public void EncodeCharEnumeratorOverload(string input, string expected) + public void EncodeCharEnumeratorOverload(string? input, string expected) { using var writer = new StringWriter(); diff --git a/source/Handlebars.Test/HtmlEncoderTests.cs b/source/Handlebars.Test/HtmlEncoderTests.cs index d5d358df..5bdaec83 100644 --- a/source/Handlebars.Test/HtmlEncoderTests.cs +++ b/source/Handlebars.Test/HtmlEncoderTests.cs @@ -45,7 +45,7 @@ public void EscapeCorrectCharacters(string input, string expected) [InlineData("\"", """)] [InlineData("&a&", "&a&")] [InlineData("a&a", "a&a")] - public void EncodeTest(string input, string expected) + public void EncodeTest(string? input, string expected) { // Arrange using var writer = new StringWriter(); @@ -62,7 +62,7 @@ public void EncodeTest(string input, string expected) [InlineData("", "")] [InlineData("a", "a")] [InlineData("<", "<")] - public void EncodeStringBuilderOverload(string input, string expected) + public void EncodeStringBuilderOverload(string? input, string expected) { using var writer = new StringWriter(); @@ -77,7 +77,7 @@ public void EncodeStringBuilderOverload(string input, string expected) [InlineData(null, "")] [InlineData("a", "a")] [InlineData("<", "<")] - public void EncodeCharEnumeratorOverload(string input, string expected) + public void EncodeCharEnumeratorOverload(string? input, string expected) { using var writer = new StringWriter(); diff --git a/source/Handlebars.Test/IssueTests.cs b/source/Handlebars.Test/IssueTests.cs index 2a69d416..ef171e93 100644 --- a/source/Handlebars.Test/IssueTests.cs +++ b/source/Handlebars.Test/IssueTests.cs @@ -374,7 +374,7 @@ public void Invoke(in EncodedTextWriter output, in HelperOptions options, in Con { var undefinedBindingResult = arguments.At(0); var separator = arguments.At(1); - var values = Substring.TrimStart(undefinedBindingResult.Value, '['); + var values = Substring.TrimStart(undefinedBindingResult!.Value, '['); values = Substring.TrimEnd(values, ']'); var substrings = Substring.Split(values, ','); var extendedEnumerator = ExtendedEnumerator.Create(substrings); @@ -478,8 +478,8 @@ public object Invoke(in BlockHelperOptions options, in Context context, in Argum public void Invoke(in EncodedTextWriter output, in BlockHelperOptions options, in Context context, in Arguments arguments) { - if (!(bool) options.Data["__switchBlock"]) throw new InvalidOperationException(); - if((bool) options.Data["__switchCaseMatched"]) return; + if (!(bool) options.Data["__switchBlock"]!) throw new InvalidOperationException(); + if((bool) options.Data["__switchCaseMatched"]!) return; var value = options.Data["switchValue"]; if(!Equals(value, arguments[0])) return; @@ -501,8 +501,8 @@ public object Invoke(in BlockHelperOptions options, in Context context, in Argum public void Invoke(in EncodedTextWriter output, in BlockHelperOptions options, in Context context, in Arguments arguments) { - if (!(bool) options.Data["__switchBlock"]) throw new InvalidOperationException(); - if((bool) options.Data["__switchCaseMatched"]) return; + if (!(bool) options.Data["__switchBlock"]!) throw new InvalidOperationException(); + if((bool) options.Data["__switchCaseMatched"]!) return; options.Template(output, options.Frame); // execute `default` in switch context } @@ -660,12 +660,12 @@ public void SegmentLiteralNotationTest(string template, string expected, Diction private class ClassWithAList { - public IEnumerable TheList { get; set; } + public IEnumerable TheList { get; set; } = null!; } private class ClassWithAListAndOtherMembers { - public IEnumerable TheList { get; set; } + public IEnumerable TheList { get; set; } = null!; public bool SomeBool { get; set; } public string SomeString { get; set; } = "I shouldn't show up!"; } diff --git a/source/Handlebars.Test/Issues/Issue285Tests.cs b/source/Handlebars.Test/Issues/Issue285Tests.cs index 11c28e02..efa9d4f2 100644 --- a/source/Handlebars.Test/Issues/Issue285Tests.cs +++ b/source/Handlebars.Test/Issues/Issue285Tests.cs @@ -59,7 +59,7 @@ public void IfWithIncludeZeroTrue_NullValue_StillTreatedAsFalsy() { var source = "{{#if value includeZero=true}}yes{{else}}no{{/if}}"; var template = Handlebars.Compile(source); - var result = template(new { value = (object)null }); + var result = template(new { value = (object?)null }); Assert.Equal("no", result); } diff --git a/source/Handlebars.Test/Issues/Issue595Tests.cs b/source/Handlebars.Test/Issues/Issue595Tests.cs index 806065cd..4fda122a 100644 --- a/source/Handlebars.Test/Issues/Issue595Tests.cs +++ b/source/Handlebars.Test/Issues/Issue595Tests.cs @@ -13,7 +13,7 @@ public class Issue595Tests public void BlockParamFromWithShouldBePassableToHelperInInnerEach() { var handlebars = Handlebars.Create(); - var receivedArgs = new List(); + var receivedArgs = new List(); handlebars.RegisterHelper("Getattributes", (context, arguments) => { @@ -55,7 +55,7 @@ public void BlockParamTypedAccessShouldNotThrowWhenPassedToHelper() // The bug manifests when field resolves to UndefinedBindingResult and the helper // uses arguments.At() (typed access) which calls TypeConverter.ConvertTo. var handlebars = Handlebars.Create(); - var receivedArgs = new List(); + var receivedArgs = new List(); handlebars.RegisterHelper("Getattributes", (context, arguments) => { @@ -96,7 +96,7 @@ public void BlockParamFromWithShouldBePassableToHelperInInnerEachMultipleIterati // This test uses more iterations to increase the chance of pool reuse, // which is what triggers the stale-data bug. var handlebars = Handlebars.Create(); - var receivedArgs = new List(); + var receivedArgs = new List(); handlebars.RegisterHelper("Getattributes", (context, arguments) => { diff --git a/source/Handlebars.Test/IteratorTests.cs b/source/Handlebars.Test/IteratorTests.cs index 84339f7b..10b6e2d8 100644 --- a/source/Handlebars.Test/IteratorTests.cs +++ b/source/Handlebars.Test/IteratorTests.cs @@ -287,7 +287,7 @@ public void NullObject() var template = Handlebars.Compile(source); var data = new { - people = (object)null + people = (object?)null }; var result = template(data); Assert.Equal("Hello, (no one listed)", result); @@ -300,7 +300,7 @@ public void NullSequence() var template = Handlebars.Compile(source); var data = new { - people = (object[])null + people = (object[]?)null }; var result = template(data); Assert.Equal("Hello, (no one listed)", result); diff --git a/source/Handlebars.Test/NumericLiteralTests.cs b/source/Handlebars.Test/NumericLiteralTests.cs index 552de90f..74f52d8a 100644 --- a/source/Handlebars.Test/NumericLiteralTests.cs +++ b/source/Handlebars.Test/NumericLiteralTests.cs @@ -9,13 +9,13 @@ public NumericLiteralTests() { Handlebars.RegisterHelper("numericLiteralAdd", (writer, context, args) => { - var arr = args.AsEnumerable().Select(a => (object)int.Parse(a.ToString())); + var arr = args.AsEnumerable().Select(a => (object)int.Parse(a!.ToString()!)); writer.Write(arr.Aggregate(0, (a, i) => a + (int)i)); }); Handlebars.RegisterHelper("longAdd", (writer, context, args) => { - var arr = args.AsEnumerable().Select(a => long.Parse(a.ToString())); + var arr = args.AsEnumerable().Select(a => long.Parse(a!.ToString()!)); var sum = arr.Sum(); writer.Write(sum); }); diff --git a/source/Handlebars.Test/PartialResolverTests.cs b/source/Handlebars.Test/PartialResolverTests.cs index 3f72e410..0e716869 100644 --- a/source/Handlebars.Test/PartialResolverTests.cs +++ b/source/Handlebars.Test/PartialResolverTests.cs @@ -6,7 +6,7 @@ public class PartialResolverTests { public class CustomPartialResolver : IPartialTemplateResolver { - public bool TryRegisterPartial(IHandlebars env, string partialName, string templatePath) + public bool TryRegisterPartial(IHandlebars env, string? partialName, string? templatePath) { if (partialName == "person") { diff --git a/source/Handlebars.Test/PartialTests.cs b/source/Handlebars.Test/PartialTests.cs index 9fb2d410..1f8b9fe3 100644 --- a/source/Handlebars.Test/PartialTests.cs +++ b/source/Handlebars.Test/PartialTests.cs @@ -212,11 +212,11 @@ public void BasicPartialWithSubExpressionParameters() Handlebars.RegisterHelper("_", (output, context, arguments) => { - output.Write(arguments[0].ToString()); + output.Write(arguments[0]!.ToString()); if (arguments.Length > 1) { - var hash = arguments[1] as Dictionary; + var hash = (Dictionary) arguments[1]!; output.Write(hash["arg1"]); } }); @@ -280,7 +280,7 @@ public void DynamicPartialWithHelperArguments() Handlebars.RegisterHelper("concat", (writer, context, args) => { - var hash = args[2] as Dictionary; + var hash = (Dictionary) args[2]!; writer.WriteSafeString(string.Concat(args[0], args[1], hash["item1"], hash["item2"])); }); diff --git a/source/Handlebars.Test/PathInfoTests.cs b/source/Handlebars.Test/PathInfoTests.cs index 9eb9fb06..688e69b9 100644 --- a/source/Handlebars.Test/PathInfoTests.cs +++ b/source/Handlebars.Test/PathInfoTests.cs @@ -17,7 +17,7 @@ public void SimplePath(string input) Assert.Equal(input, pathInfo.Path); Assert.Equal(input.Trim('[', ']'), pathInfo.TrimmedPath); Assert.False(pathInfo.IsVariable); - var chainSegment = Assert.Single(pathInfo.Segments.SelectMany(o => o.PathChain)); + var chainSegment = Assert.Single(pathInfo.Segments!.SelectMany(o => o.PathChain)); Assert.NotNull(chainSegment); Assert.Equal(input, chainSegment.ToString()); } @@ -32,7 +32,7 @@ public void DotPath() Assert.Equal(input, pathInfo.Path); var parts = input.Split('.'); - var pathChain = pathInfo.Segments.SelectMany(o => o.PathChain).ToArray(); + var pathChain = pathInfo.Segments!.SelectMany(o => o.PathChain).ToArray(); for (var index = 0; index < pathChain.Length; index++) { Assert.Equal(parts[index], pathChain[index]); @@ -59,7 +59,7 @@ public void SlashPath(string input, string[] expected) Assert.Equal(input, pathInfo.Path); - for (var index = 0; index < pathInfo.Segments.Length; index++) + for (var index = 0; index < pathInfo.Segments!.Length; index++) { Assert.Equal(expected[index], pathInfo.Segments[index].ToString()); } diff --git a/source/Handlebars.Test/RawHelperTests.cs b/source/Handlebars.Test/RawHelperTests.cs index 9ccd56b4..2cafa4cd 100644 --- a/source/Handlebars.Test/RawHelperTests.cs +++ b/source/Handlebars.Test/RawHelperTests.cs @@ -13,7 +13,7 @@ public void RawBlockHelper() var source = "{{{{rawBlockHelper}}}} {{foo}} {{{foo}}}{{{{otherRawBlockHelper}}}} {{ bar }}{{{bar}}}{{{{/otherRawBlockHelper}}}}{{{{/rawBlockHelper}}}}"; inst.RegisterHelper("rawBlockHelper", (writer, options, context, arguments) => { - options.Template(writer, null); + options.Template(writer, null!); }); var data = new @@ -39,8 +39,8 @@ public void RawBlockHelperWithArguments() inst.RegisterHelper("rawBlockHelper", (writer, options, context, arguments) => { writer.Write(arguments[0]); - options.Template(writer, null); - writer.Write((arguments[1] as IDictionary)["bar"]); + options.Template(writer, null!); + writer.Write(((IDictionary) arguments[1]!)["bar"]); }); var data = new @@ -67,7 +67,7 @@ public void HtmlIsNotEscapedInsideRawHelper() inst.RegisterHelper("rawBlockHelper", (writer, options, context, arguments) => { writer.Write(arguments[0]); - options.Template(writer, null); + options.Template(writer, null!); }); var data = new @@ -88,8 +88,8 @@ public void HtmlIsNotEscapedInsideRawHelperHashArgs() inst.RegisterHelper("rawBlockHelper", (writer, options, context, arguments) => { writer.Write(arguments[0]); - options.Template(writer, null); - writer.Write((arguments[1] as IDictionary)["foo"]); + options.Template(writer, null!); + writer.Write(((IDictionary) arguments[1]!)["foo"]); }); var data = new @@ -109,7 +109,7 @@ public void RawHelperShouldNotMangleArgumentsInBody() var source = "{{{{rawBlockHelper}}}}{{someHelper fooArg fooHashArg='foo' fooHashArgDoubleQuote=\"foo!\" barHashArg=unquotedValue bazHashArg=@root.baz.nested}}{{{{/rawBlockHelper}}}}"; inst.RegisterHelper("rawBlockHelper", (writer, options, context, arguments) => { - options.Template(writer, null); + options.Template(writer, null!); }); var template = inst.Compile(source); @@ -125,7 +125,7 @@ public void RawHelperShouldNotMangleArgumentsInBodyIfAnExistingHelperIsReference var source = "{{{{rawBlockHelper}}}}{{someHelper fooArg fooHashArg='foo' fooHashArgDoubleQuote=\"foo!\" barHashArg=unquotedValue bazHashArg=@root.baz.nested}}{{{{/rawBlockHelper}}}}"; inst.RegisterHelper("rawBlockHelper", (writer, options, context, arguments) => { - options.Template(writer, null); + options.Template(writer, null!); }); inst.RegisterHelper("someHelper", (writer, context, parameters) => @@ -146,8 +146,8 @@ public void TestNonClosingRawBlockExpressionException() inst.RegisterHelper("rawBlockHelper", (writer, options, context, arguments) => { writer.Write(arguments[0]); - options.Template(writer, null); - writer.Write((arguments[1] as IDictionary)["bar"]); + options.Template(writer, null!); + writer.Write(((IDictionary) arguments[1]!)["bar"]); }); Assert.Throws(() => diff --git a/source/Handlebars.Test/SubExpressionTests.cs b/source/Handlebars.Test/SubExpressionTests.cs index 2aa33e81..187ce1fa 100644 --- a/source/Handlebars.Test/SubExpressionTests.cs +++ b/source/Handlebars.Test/SubExpressionTests.cs @@ -80,7 +80,7 @@ public void BasicSubExpressionWithNumericLiteralArguments() { var handlebars = Handlebars.Create(); handlebars.RegisterHelper("helper", (writer, context, args) => writer.Write($"Math {args[0]}")); - handlebars.RegisterHelper("subhelper", (writer, context, args) => writer.Write((int)args[0] + (int)args[1])); + handlebars.RegisterHelper("subhelper", (writer, context, args) => writer.Write((int)args[0]! + (int)args[1]!)); var source = "{{helper (subhelper 1 2)}}"; @@ -122,7 +122,7 @@ public void TwoBasicSubExpressionsWithNumericLiteralArguments() }); handlebars.RegisterHelper("add", (writer, context, args) => { - writer.Write((int)args[0] + (int)args[1]); + writer.Write((int)args[0]! + (int)args[1]!); }); var source = "{{math (add 1 2) (add 3 4)}}"; @@ -145,7 +145,7 @@ public void BasicSubExpressionWithNumericAndStringLiteralArguments() }); handlebars.RegisterHelper("add", (writer, context, args) => { - writer.Write((int)args[0] + (int)args[1]); + writer.Write((int)args[0]! + (int)args[1]!); }); var source = "{{write (add 1 2) \"hello\"}}"; diff --git a/source/Handlebars.Test/ViewEngine/ViewEngineTests.cs b/source/Handlebars.Test/ViewEngine/ViewEngineTests.cs index a7bdebc1..fb3b21c0 100644 --- a/source/Handlebars.Test/ViewEngine/ViewEngineTests.cs +++ b/source/Handlebars.Test/ViewEngine/ViewEngineTests.cs @@ -354,7 +354,7 @@ private class MyDynamicModel: DynamicObject public override IEnumerable GetDynamicMemberNames() => _properties.Keys; public override bool TryGetMember(GetMemberBinder binder, out object result) => - _properties.TryGetValue(binder.Name, out result); + _properties.TryGetValue(binder.Name, out result!); } //We have a fake file system. Difference frameworks and apps will use @@ -367,7 +367,7 @@ public void Add(string fileName, string fileContent) files[Sanitise(fileName)] = fileContent; } - public override string GetFileContent(string filename) + public override string? GetFileContent(string filename) { if (!files.ContainsKey(Sanitise(filename))) return null; return files[Sanitise(filename)]; diff --git a/source/Handlebars/Arguments.cs b/source/Handlebars/Arguments.cs index b344a4d0..7fda2b5a 100644 --- a/source/Handlebars/Arguments.cs +++ b/source/Handlebars/Arguments.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using HandlebarsDotNet.Compiler; @@ -12,17 +13,16 @@ namespace HandlebarsDotNet /// /// Mimics behavior however in most cases does not require memory allocation. /// - public readonly struct Arguments : IEquatable, IEnumerable + public readonly struct Arguments : IEquatable, IEnumerable { - private readonly object[] _array; - private readonly bool _useArray; + private readonly object[]? _array; - private readonly object _element0; - private readonly object _element1; - private readonly object _element2; - private readonly object _element3; - private readonly object _element4; - private readonly object _element5; + private readonly object? _element0; + private readonly object? _element1; + private readonly object? _element2; + private readonly object? _element3; + private readonly object? _element4; + private readonly object? _element5; public readonly int Length; @@ -33,7 +33,6 @@ namespace HandlebarsDotNet [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Arguments(int dummy = 0) : this() { - _useArray = false; _array = null; _element0 = null; _element1 = null; @@ -45,9 +44,8 @@ internal Arguments(int dummy = 0) : this() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Arguments(object arg1) : this() + public Arguments(object? arg1) : this() { - _useArray = false; _array = null; _element0 = arg1; _element1 = null; @@ -59,9 +57,8 @@ public Arguments(object arg1) : this() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Arguments(object arg1, object arg2) : this() + public Arguments(object? arg1, object? arg2) : this() { - _useArray = false; _array = null; _element0 = arg1; _element1 = arg2; @@ -73,9 +70,8 @@ public Arguments(object arg1, object arg2) : this() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Arguments(object arg1, object arg2, object arg3) : this() + public Arguments(object? arg1, object? arg2, object? arg3) : this() { - _useArray = false; _array = null; _element0 = arg1; _element1 = arg2; @@ -87,9 +83,8 @@ public Arguments(object arg1, object arg2, object arg3) : this() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Arguments(object arg1, object arg2, object arg3, object arg4) : this() + public Arguments(object? arg1, object? arg2, object? arg3, object? arg4) : this() { - _useArray = false; _array = null; _element0 = arg1; _element1 = arg2; @@ -101,9 +96,8 @@ public Arguments(object arg1, object arg2, object arg3, object arg4) : this() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Arguments(object arg1, object arg2, object arg3, object arg4, object arg5) : this() + public Arguments(object? arg1, object? arg2, object? arg3, object? arg4, object? arg5) : this() { - _useArray = false; _array = null; _element0 = arg1; _element1 = arg2; @@ -115,9 +109,8 @@ public Arguments(object arg1, object arg2, object arg3, object arg4, object arg5 } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Arguments(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6) : this() + public Arguments(object? arg1, object? arg2, object? arg3, object? arg4, object? arg5, object? arg6) : this() { - _useArray = false; _array = null; _element0 = arg1; _element1 = arg2; @@ -131,7 +124,6 @@ public Arguments(object arg1, object arg2, object arg3, object arg4, object arg5 [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arguments(object[] args) : this() { - _useArray = true; _array = args; Length = args.Length; @@ -144,7 +136,7 @@ public Arguments(object[] args) : this() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IEnumerator GetEnumerator() => Enumerator.Create(this); + public IEnumerator GetEnumerator() => Enumerator.Create(this); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -153,9 +145,9 @@ public Arguments(object[] args) : this() /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IEnumerable AsEnumerable() => new Enumerable(this); + public IEnumerable AsEnumerable() => new Enumerable(this); - public IReadOnlyDictionary Hash + public IReadOnlyDictionary Hash { [MethodImpl(MethodImplOptions.AggressiveInlining)] get @@ -165,14 +157,14 @@ public IReadOnlyDictionary Hash } } - public object this[int index] + public object? this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (index < 0 || index >= Length) Throw.IndexOutOfRangeException(); - - if(_useArray) return _array[index]; + + if (_array != null) return _array[index]; return index switch { @@ -187,22 +179,22 @@ public object this[int index] } } - public object this[string name] + public object? this[string name] { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Hash?[name]; + get => Hash[name]; } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T At(in int index) + public T? At(in int index) { var obj = this[index]; if (obj is null) return default; if (obj is T value) return value; var converter = TypeDescriptor.GetConverter(obj.GetType()); - return (T) converter.ConvertTo(obj, typeof(T)); + return (T?) converter.ConvertTo(obj, typeof(T)); } public static implicit operator Arguments(object[] array) @@ -214,7 +206,7 @@ public static implicit operator Arguments(object[] array) public bool Equals(Arguments other) { - if (_useArray && _useArray == other._useArray) + if (_array != null && other._array != null) { if (Length != other.Length || _array.Length != other._array.Length) return false; for (int i = 0; i < _array.Length; i++) @@ -234,7 +226,7 @@ public bool Equals(Arguments other) && Equals(_element5, other._element5); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Arguments other && Equals(other); } @@ -254,19 +246,19 @@ public override int GetHashCode() } } - private sealed class Enumerable : IEnumerable + private sealed class Enumerable : IEnumerable { private readonly Arguments _arguments; public Enumerable(in Arguments arguments) => _arguments = arguments; - public IEnumerator GetEnumerator() => Enumerator.Create(_arguments); + public IEnumerator GetEnumerator() => Enumerator.Create(_arguments); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - private sealed class Enumerator : IEnumerator + private sealed class Enumerator : IEnumerator { private static readonly InternalObjectPool Pool = new InternalObjectPool(new Policy()); @@ -292,13 +284,13 @@ private Enumerator() public void Reset() => _index = -1; - public object Current + public object? Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (_index == -1) return null; - if(_arguments._useArray) return _arguments._array[_index]; + if (_arguments._array != null) return _arguments._array[_index]; return _index switch { @@ -334,7 +326,8 @@ public bool Return(Enumerator item) private static class Throw { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static object IndexOutOfRangeException(string message = null) => throw new ArgumentOutOfRangeException(message); + [DoesNotReturn] + public static object IndexOutOfRangeException(string? message = null) => throw new ArgumentOutOfRangeException(message); } } } \ No newline at end of file diff --git a/source/Handlebars/BindingContext.cs b/source/Handlebars/BindingContext.cs index 19e36731..f7875884 100644 --- a/source/Handlebars/BindingContext.cs +++ b/source/Handlebars/BindingContext.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using HandlebarsDotNet.Collections; using HandlebarsDotNet.Compiler; @@ -24,8 +25,8 @@ private BindingContext() BlockHelpers = new CascadeIndex, StringEqualityComparer>(new StringEqualityComparer()); Bag = new CascadeIndex(new StringEqualityComparer(StringComparison.OrdinalIgnoreCase)); - ContextDataObject = new FixedSizeDictionary(16, 7, ChainSegment.EqualityComparer); - BlockParamsObject = new FixedSizeDictionary(16, 7, ChainSegment.EqualityComparer); + ContextDataObject = new FixedSizeDictionary(16, 7, ChainSegment.EqualityComparer); + BlockParamsObject = new FixedSizeDictionary(16, 7, ChainSegment.EqualityComparer); Descriptor = new DeferredValue(this, context => { @@ -34,10 +35,10 @@ private BindingContext() } internal CascadeIndex Bag { get; } - internal FixedSizeDictionary ContextDataObject { get; } - internal FixedSizeDictionary BlockParamsObject { get; } + internal FixedSizeDictionary ContextDataObject { get; } + internal FixedSizeDictionary BlockParamsObject { get; } - internal void SetDataObject(object data) + internal void SetDataObject(object? data) { if(data == null) return; @@ -107,7 +108,7 @@ out WellKnownVariables[(int) WellKnownVariable.Parent] PopulateHash(dictionary, ParentContext.Value); } - internal ICompiledHandlebarsConfiguration Configuration { get; private set; } + internal ICompiledHandlebarsConfiguration Configuration { get; private set; } = null!; internal CascadeIndex, StringEqualityComparer> InlinePartialTemplates { get; } @@ -115,11 +116,11 @@ out WellKnownVariables[(int) WellKnownVariable.Parent] internal CascadeIndex, StringEqualityComparer> BlockHelpers { get; } - internal TemplateDelegate PartialBlockTemplate { get; set; } + internal TemplateDelegate? PartialBlockTemplate { get; set; } internal short PartialDepth { get; set; } - - public object Value { get; set; } + + public object? Value { get; set; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public BlockParamsValues BlockParams(ChainSegment[] blockParamsVariables) => new BlockParamsValues(this, blockParamsVariables); @@ -131,24 +132,24 @@ out WellKnownVariables[(int) WellKnownVariable.Parent] /// public IIndexed Extensions => Bag; - internal BindingContext ParentContext { get; private set; } + internal BindingContext? ParentContext { get; private set; } - internal BindingContext Root { get; private set; } + internal BindingContext Root { get; private set; } = null!; - internal bool TryGetVariable(ChainSegment segment, out object value) + internal bool TryGetVariable(ChainSegment segment, [MaybeNullWhen(false)] out object value) { if (segment.WellKnownVariable != WellKnownVariable.None) { var wellKnownVariable = WellKnownVariables[(int) segment.WellKnownVariable]; return BlockParamsObject.TryGetValue(wellKnownVariable, out value) - || (Descriptor.Value.MemberAccessor?.TryGetValue(Value, segment, out value) ?? false); + || (Descriptor.Value.MemberAccessor?.TryGetValue(Value!, segment, out value) ?? false); } return BlockParamsObject.TryGetValue(segment, out value) - || (Descriptor.Value.MemberAccessor?.TryGetValue(Value, segment, out value) ?? false); + || (Descriptor.Value.MemberAccessor?.TryGetValue(Value!, segment, out value) ?? false); } - internal bool TryGetContextVariable(ChainSegment segment, out object value) + internal bool TryGetContextVariable(ChainSegment segment, [MaybeNullWhen(false)] out object value) { if (segment.WellKnownVariable != WellKnownVariable.None) { @@ -174,13 +175,13 @@ internal bool TryGetContextVariable(ChainSegment segment, out object value) || ContextDataObject.TryGetValue(segment, out value); } - internal BindingContext CreateChildContext(object value, TemplateDelegate partialBlockTemplate = null) + internal BindingContext CreateChildContext(object value, TemplateDelegate? partialBlockTemplate = null) { return Create(Configuration, value, this, partialBlockTemplate ?? PartialBlockTemplate); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BindingContext CreateFrame(object value = null) + public BindingContext CreateFrame(object? value = null) { return Create(Configuration, value, this, PartialBlockTemplate); } diff --git a/source/Handlebars/BlockHelperOptions.cs b/source/Handlebars/BlockHelperOptions.cs index 86f12813..fc86b89c 100644 --- a/source/Handlebars/BlockHelperOptions.cs +++ b/source/Handlebars/BlockHelperOptions.cs @@ -48,14 +48,14 @@ public string Template() OriginalTemplate(encodedTextWriter, Frame); - return encodedTextWriter.ToString(); + return writer.ToString(); } /// /// BlockHelper body /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Template(in EncodedTextWriter writer, object context) + public void Template(in EncodedTextWriter writer, object? context) { if (context is BindingContext bindingContext) { @@ -90,14 +90,14 @@ public string Inverse() OriginalInverse(encodedTextWriter, Frame); - return encodedTextWriter.ToString(); + return writer.ToString(); } /// /// BlockHelper body /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Inverse(in EncodedTextWriter writer, object context) + public void Inverse(in EncodedTextWriter writer, object? context) { if (context is BindingContext bindingContext) { @@ -122,7 +122,7 @@ public void Inverse(in EncodedTextWriter writer, object context) public void Inverse(in EncodedTextWriter writer, BindingContext context) => OriginalInverse(writer, context); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BindingContext CreateFrame(object value = null) => Frame.CreateFrame(value); + public BindingContext CreateFrame(object? value = null) => Frame.CreateFrame(value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public BindingContext CreateFrame(Context value) => Frame.CreateFrame(value.Value); diff --git a/source/Handlebars/Collections/CascadeIndex.cs b/source/Handlebars/Collections/CascadeIndex.cs index 6d3a592e..76227c6f 100644 --- a/source/Handlebars/Collections/CascadeIndex.cs +++ b/source/Handlebars/Collections/CascadeIndex.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; @@ -16,17 +17,18 @@ namespace HandlebarsDotNet.Collections [DebuggerDisplay("Count = {Count}")] public class CascadeIndex : IIndexed where TComparer : IEqualityComparer + where TKey : notnull { private readonly TComparer _comparer; - public IReadOnlyIndexed Outer { get; set; } - private DictionarySlim _inner; + public IReadOnlyIndexed? Outer { get; set; } + private DictionarySlim? _inner; public CascadeIndex(TComparer comparer) : this(null, comparer) { } - public CascadeIndex(IReadOnlyIndexed outer, TComparer comparer) + public CascadeIndex(IReadOnlyIndexed? outer, TComparer comparer) { _comparer = comparer; Outer = outer; @@ -53,7 +55,7 @@ public bool ContainsKey(in TKey key) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetValue(in TKey key, out TValue value) + public bool TryGetValue(in TKey key, [MaybeNullWhen(false)] out TValue value) { value = default; return (_inner?.TryGetValue(key, out value) ?? false) @@ -97,7 +99,7 @@ private IEnumerable> OuterEnumerable() using var outerEnumerator = Outer.GetEnumerator(); while (outerEnumerator.MoveNext()) { - if (_inner.ContainsKey(outerEnumerator.Current.Key)) continue; + if (_inner != null && _inner.ContainsKey(outerEnumerator.Current.Key)) continue; yield return outerEnumerator.Current; } } @@ -106,7 +108,8 @@ private IEnumerable> OuterEnumerable() private static class Throw { - public static void KeyNotFoundException(string message, Exception exception = null) => throw new KeyNotFoundException(message, exception); + [DoesNotReturn] + public static void KeyNotFoundException(string message, Exception? exception = null) => throw new KeyNotFoundException(message, exception); } } } \ No newline at end of file diff --git a/source/Handlebars/Collections/DictionarySlim.cs b/source/Handlebars/Collections/DictionarySlim.cs index 6325e9c0..07c652e1 100644 --- a/source/Handlebars/Collections/DictionarySlim.cs +++ b/source/Handlebars/Collections/DictionarySlim.cs @@ -6,6 +6,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace HandlebarsDotNet.Collections @@ -14,6 +15,7 @@ namespace HandlebarsDotNet.Collections internal class DictionarySlim : IIndexed where TComparer: IEqualityComparer + where TKey : notnull { private readonly TComparer _comparer; @@ -141,7 +143,7 @@ public bool ContainsKey(in TKey key) /// Key to look for /// Value found, otherwise default(TValue) /// true if the key is present, otherwise false - public bool TryGetValue(in TKey key, out TValue value) + public bool TryGetValue(in TKey key, [MaybeNullWhen(false)] out TValue value) { if (key is null) throw new ArgumentNullException(nameof(key)); var entries = _entries; @@ -320,6 +322,7 @@ public void Dispose() { } private static class Throw { [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] public static void ConcurrentOperationsNotSupported() => throw new InvalidOperationException("ConcurrentOperationsNotSupported"); } } diff --git a/source/Handlebars/Collections/ExtendedEnumerator.cs b/source/Handlebars/Collections/ExtendedEnumerator.cs index 9002e460..082908d6 100644 --- a/source/Handlebars/Collections/ExtendedEnumerator.cs +++ b/source/Handlebars/Collections/ExtendedEnumerator.cs @@ -18,7 +18,7 @@ public static ExtendedEnumerator Create(TEnumerator private readonly IEnumerator _enumerator; - private T _next; + private T? _next; private int _index; private bool _hasNext; @@ -50,7 +50,7 @@ private void PerformIteration() if (!_enumerator.MoveNext()) { Current = _hasNext - ? new EnumeratorValue(_next, _index++, true) + ? new EnumeratorValue(_next!, _index++, true) : new EnumeratorValue(); _hasNext = false; @@ -65,7 +65,7 @@ private void PerformIteration() return; } - Current = new EnumeratorValue(_next, _index++, false); + Current = new EnumeratorValue(_next!, _index++, false); _next = (T) _enumerator.Current; } } @@ -75,7 +75,7 @@ public ref struct ExtendedEnumerator { private TEnumerator _enumerator; // NOSONAR S2933 — cannot be readonly: struct TEnumerator mutates on MoveNext() and readonly would cause a defensive copy - private T _next; + private T? _next; private int _index; private bool _hasNext; @@ -107,7 +107,7 @@ private void PerformIteration() if (!_enumerator.MoveNext()) { Current = _hasNext - ? new EnumeratorValue(_next, _index++, true) + ? new EnumeratorValue(_next!, _index++, true) : new EnumeratorValue(); _hasNext = false; @@ -122,7 +122,7 @@ private void PerformIteration() return; } - Current = new EnumeratorValue(_next, _index++, false); + Current = new EnumeratorValue(_next!, _index++, false); _next = _enumerator.Current; } } diff --git a/source/Handlebars/Collections/FixedSizeDictionary.cs b/source/Handlebars/Collections/FixedSizeDictionary.cs index d209155a..01e43cb2 100644 --- a/source/Handlebars/Collections/FixedSizeDictionary.cs +++ b/source/Handlebars/Collections/FixedSizeDictionary.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace HandlebarsDotNet.Collections @@ -17,7 +18,6 @@ public class FixedSizeDictionary : IIndexed, IReadOnlyDictionary where TKey : notnull - where TValue : notnull where TComparer : notnull, IEqualityComparer { private const int MaximumSize = 1024; @@ -154,7 +154,7 @@ public bool ContainsKey(in TKey key) /// /// Performs lookup at guarantied O(1) ignoring actual key comparison /// - public bool TryGetValue(in EntryIndex keyIndex, out TValue value) + public bool TryGetValue(in EntryIndex keyIndex, [MaybeNullWhen(false)] out TValue value) { if (_count == 0 || keyIndex.Version != _version) { @@ -176,7 +176,7 @@ public bool TryGetValue(in EntryIndex keyIndex, out TValue value) /// /// Performs lookup at best O(1) and worst O(m) where 'm' is number of collisions /// - public bool TryGetValue(in TKey key, out TValue value) + public bool TryGetValue(in TKey key, [MaybeNullWhen(false)] out TValue value) { if (_count == 0) { @@ -224,6 +224,7 @@ public bool TryGetValue(in TKey key, out TValue value) return false; } + [MaybeNull] public TValue this[in TKey key] { get => TryGetValue(key, out var value) ? value : default; @@ -325,6 +326,7 @@ public void AddOrReplace(in TKey key, in TValue value, out EntryIndex inde /// Gets or replaces item at a given index at O(1) /// /// + [MaybeNull] public TValue this[in EntryIndex entryIndex] { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -448,14 +450,18 @@ public IEnumerator> GetEnumerator() IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - bool IReadOnlyDictionary.TryGetValue(TKey key, out TValue value) => TryGetValue(key, out value); +#if NET8_0_OR_GREATER + bool IReadOnlyDictionary.TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) => TryGetValue(key, out value); +#else + bool IReadOnlyDictionary.TryGetValue(TKey key, out TValue value) => TryGetValue(key, out value!); +#endif TValue IReadOnlyDictionary.this[TKey key] { get { if (!TryGetIndex(key, out var index)) throw new KeyNotFoundException(); - return this[index]; + return this[index]!; } } @@ -527,6 +533,7 @@ internal Entry(Entry entry, byte version) private static class Throw { [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] public static void CapacityShouldBeEqual(string paramName) => throw new ArgumentException(" capacity should be equal to source dictionary", paramName); } } @@ -549,7 +556,7 @@ internal EntryIndex(in int index, in byte version) public bool Equals(EntryIndex other) => IsNotEmpty == other.IsNotEmpty && Version == other.Version && Index == other.Index; - public override bool Equals(object obj) + public override bool Equals(object? obj) => obj is EntryIndex other && Equals(other); public override int GetHashCode() diff --git a/source/Handlebars/Collections/IIndexed.cs b/source/Handlebars/Collections/IIndexed.cs index ac2edcbe..a7dea982 100644 --- a/source/Handlebars/Collections/IIndexed.cs +++ b/source/Handlebars/Collections/IIndexed.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace HandlebarsDotNet.Collections { /// @@ -9,6 +11,7 @@ namespace HandlebarsDotNet.Collections public interface IIndexed : IReadOnlyIndexed { void AddOrReplace(in TKey key, in TValue value); + [MaybeNull] new TValue this[in TKey key] { get; set; } void Clear(); } diff --git a/source/Handlebars/Collections/IObservableEvent.cs b/source/Handlebars/Collections/IObservableEvent.cs new file mode 100644 index 00000000..713ba49a --- /dev/null +++ b/source/Handlebars/Collections/IObservableEvent.cs @@ -0,0 +1,6 @@ +namespace HandlebarsDotNet.Collections; + +public interface IObservableEvent +{ + T Value { get; } +} \ No newline at end of file diff --git a/source/Handlebars/Collections/IReadOnlyIndexed.cs b/source/Handlebars/Collections/IReadOnlyIndexed.cs index 69b0424c..61cebb91 100644 --- a/source/Handlebars/Collections/IReadOnlyIndexed.cs +++ b/source/Handlebars/Collections/IReadOnlyIndexed.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.Collections { @@ -12,7 +13,7 @@ public interface IReadOnlyIndexed : IReadOnlyCollection> { bool ContainsKey(in TKey key); - bool TryGetValue(in TKey key, out TValue value); - TValue this[in TKey key] { get; } + bool TryGetValue(in TKey key, [MaybeNullWhen(false)] out TValue value); + TValue? this[in TKey key] { get; } } } \ No newline at end of file diff --git a/source/Handlebars/Collections/ImmutableStack.cs b/source/Handlebars/Collections/ImmutableStack.cs index 4af7ffb9..0eefddb8 100644 --- a/source/Handlebars/Collections/ImmutableStack.cs +++ b/source/Handlebars/Collections/ImmutableStack.cs @@ -9,22 +9,22 @@ namespace HandlebarsDotNet.Collections #endif internal readonly struct ImmutableStack { - private readonly Node _container; + private readonly Node? _container; [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ImmutableStack(T value, Node parent) + private ImmutableStack(T value, Node? parent) :this(Node.Create(value, parent)) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ImmutableStack(Node container) => _container = container; + private ImmutableStack(Node? container) => _container = container; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ImmutableStack Push(T value) => new ImmutableStack(value, _container); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Peek() + public T? Peek() { return _container == null ? default @@ -32,7 +32,7 @@ public T Peek() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ImmutableStack Pop(out T value) + public ImmutableStack Pop(out T? value) { if (_container == null) { @@ -53,10 +53,10 @@ private sealed class Node : IDisposable { private static readonly InternalObjectPool Pool = new InternalObjectPool(new Policy()); // NOSONAR S2743 — one pool per T is intentional: Node and Node cannot share a pool - public Node Parent; - public T Value; + public Node? Parent; + public T? Value; - public static Node Create(T value = default, Node parent = null) + public static Node Create(T? value = default, Node? parent = null) { var item = Pool.Get(); item.Value = value; diff --git a/source/Handlebars/Collections/LookupSlim.cs b/source/Handlebars/Collections/LookupSlim.cs index 5b6bac48..0c7cdf5b 100644 --- a/source/Handlebars/Collections/LookupSlim.cs +++ b/source/Handlebars/Collections/LookupSlim.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Threading; namespace HandlebarsDotNet.Collections @@ -9,6 +10,7 @@ namespace HandlebarsDotNet.Collections [DebuggerDisplay("Count = {Count}")] internal sealed class LookupSlim : IReadOnlyIndexed + where TKey : notnull where TComparer : IEqualityComparer { private readonly TComparer _comparer; @@ -36,7 +38,7 @@ public TValue GetOrAdd(TKey key, Func valueFactory : value; } - public bool TryGetValue(in TKey key, out TValue value) => _inner.TryGetValue(key, out value); + public bool TryGetValue(in TKey key, [MaybeNullWhen(false)] out TValue value) => _inner.TryGetValue(key, out value); public int Count => _inner.Count; diff --git a/source/Handlebars/Collections/ObservableEvent.cs b/source/Handlebars/Collections/ObservableEvent.cs index 97b3fa34..5317d2df 100644 --- a/source/Handlebars/Collections/ObservableEvent.cs +++ b/source/Handlebars/Collections/ObservableEvent.cs @@ -1,6 +1,6 @@ namespace HandlebarsDotNet.Collections { - public abstract class ObservableEvent + public abstract class ObservableEvent : IObservableEvent { public T Value { get; } diff --git a/source/Handlebars/Collections/ObservableIndex.cs b/source/Handlebars/Collections/ObservableIndex.cs index 0e9e27f5..68459bc2 100644 --- a/source/Handlebars/Collections/ObservableIndex.cs +++ b/source/Handlebars/Collections/ObservableIndex.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using HandlebarsDotNet.Runtime; @@ -10,35 +11,36 @@ namespace HandlebarsDotNet.Collections { [DebuggerDisplay("Count = {Count}")] public class ObservableIndex : - IObservable>, - IObserver>, + IObservable>, + IObserver>, IIndexed + where TKey : notnull where TComparer: IEqualityComparer { private readonly ReaderWriterLockSlim _observersLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); private readonly ReaderWriterLockSlim _itemsLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); - private readonly WeakCollection>> _observers; + private readonly WeakCollection>> _observers; private readonly DictionarySlim _inner; - public ObservableIndex(TComparer comparer, IReadOnlyIndexed outer = null) + public ObservableIndex(TComparer comparer, IReadOnlyIndexed? outer = null) { _inner = outer != null ? new DictionarySlim(outer, comparer) : new DictionarySlim(comparer); - _observers = new WeakCollection>>(); - if (outer is IObservable> observableDictionary) + _observers = new WeakCollection>>(); + if (outer is IObservable> observableDictionary) { observableDictionary.Subscribe(this); } } - public IDisposable Subscribe(IObserver> observer) + public IDisposable Subscribe(IObserver> observer) { using (_observersLock.WriteLock()) { _observers.Add(observer); } - var disposableContainer = new DisposableContainer>>, ReaderWriterLockSlim>( + var disposableContainer = new DisposableContainer>>, ReaderWriterLockSlim>( _observers, _observersLock, (observers, @lock) => { using (@lock.WriteLock()) @@ -51,7 +53,7 @@ public IDisposable Subscribe(IObserver> observer) return disposableContainer; } - private void Publish(ObservableEvent @event) + private void Publish(IObservableEvent @event) { using (_observersLock.ReadLock()) { @@ -98,7 +100,7 @@ public bool ContainsKey(in TKey key) } } - public bool TryGetValue(in TKey key, out TValue value) + public bool TryGetValue(in TKey key, [MaybeNullWhen(false)] out TValue value) { using (_itemsLock.ReadLock()) { @@ -106,6 +108,7 @@ public bool TryGetValue(in TKey key, out TValue value) } } + [MaybeNull] public TValue this[in TKey key] { get => TryGetValue(key, out var value) ? value : default; @@ -148,7 +151,7 @@ public void OnError(Exception error) // nothing to do here } - public void OnNext(ObservableEvent value) + public void OnNext(IObservableEvent value) { switch (value) { @@ -172,7 +175,7 @@ internal class DictionaryAddedObservableEvent : ObservableEvent Key = key; } - internal class DictionaryClearedObservableEvent : ObservableEvent + internal class DictionaryClearedObservableEvent : ObservableEvent { public DictionaryClearedObservableEvent() : base(default) {} } diff --git a/source/Handlebars/Collections/ObservableList.cs b/source/Handlebars/Collections/ObservableList.cs index 124c4357..a240676a 100644 --- a/source/Handlebars/Collections/ObservableList.cs +++ b/source/Handlebars/Collections/ObservableList.cs @@ -10,19 +10,19 @@ namespace HandlebarsDotNet.Collections [DebuggerDisplay("Count = {Count}")] public class ObservableList : IAppendOnlyList, - IObservable>, - IObserver> + IObservable>, + IObserver> { private readonly ReaderWriterLockSlim _observersLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); private readonly ReaderWriterLockSlim _itemsLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); - private readonly WeakCollection>> _observers = new WeakCollection>>(); + private readonly WeakCollection>> _observers = new WeakCollection>>(); private readonly List _inner; - public ObservableList(IEnumerable list = null) + public ObservableList(IEnumerable? list = null) { _inner = list != null ? new List(list) : new List(); - if (list is IObservable> observableList) + if (list is IObservable> observableList) { observableList.Subscribe(this); } @@ -76,14 +76,14 @@ public T this[int index] } } - public IDisposable Subscribe(IObserver> observer) + public IDisposable Subscribe(IObserver> observer) { using (_observersLock.WriteLock()) { _observers.Add(observer); } - var disposableContainer = new DisposableContainer>>, ReaderWriterLockSlim>( + var disposableContainer = new DisposableContainer>>, ReaderWriterLockSlim>( _observers, _observersLock, (observers, @lock) => { using (@lock.WriteLock()) @@ -106,7 +106,7 @@ public void OnError(Exception error) // nothing to do here } - public void OnNext(ObservableEvent value) + public void OnNext(IObservableEvent value) { switch (value) { @@ -119,7 +119,7 @@ public void OnNext(ObservableEvent value) } } - private void Publish(ObservableEvent @event) + private void Publish(IObservableEvent @event) { using (_observersLock.ReadLock()) { diff --git a/source/Handlebars/Collections/ObserverBuilder.cs b/source/Handlebars/Collections/ObserverBuilder.cs index 0db655c1..0c3ee877 100644 --- a/source/Handlebars/Collections/ObserverBuilder.cs +++ b/source/Handlebars/Collections/ObserverBuilder.cs @@ -4,6 +4,7 @@ namespace HandlebarsDotNet.Collections { internal static class ObserverBuilder + where T: notnull { public static ObserverBuilder Create(TState state) { @@ -12,13 +13,14 @@ public static ObserverBuilder Create(TState state) } internal class ObserverBuilder + where T: notnull { private readonly TState _state; private readonly Dictionary>> _handlers = new Dictionary>>(); public ObserverBuilder(TState state) => _state = state; - public ObserverBuilder OnEvent(Action handler, Func predicate = null) where TEvent: T + public ObserverBuilder OnEvent(Action handler, Func? predicate = null) where TEvent: T { if (!_handlers.TryGetValue(typeof(TEvent), out var handlers)) { diff --git a/source/Handlebars/Collections/WeakCollection.cs b/source/Handlebars/Collections/WeakCollection.cs index 45f78ba8..5a750446 100644 --- a/source/Handlebars/Collections/WeakCollection.cs +++ b/source/Handlebars/Collections/WeakCollection.cs @@ -6,7 +6,7 @@ namespace HandlebarsDotNet.Collections { public class WeakCollection : IEnumerable where T : class { - private readonly List> _store = new List>(); + private readonly List?> _store = new List?>(); private int _firstAvailableIndex = 0; public int Size => _store.Count; @@ -22,10 +22,10 @@ public void Add(T value) return; } - if (!_store[index].TryGetTarget(out _)) + if (!_store[index]!.TryGetTarget(out _)) { _firstAvailableIndex = index + 1; - _store[index].SetTarget(value); + _store[index]!.SetTarget(value); return; } } @@ -48,7 +48,7 @@ public void Remove(T value) continue; } - if (!_store[index].TryGetTarget(out var target)) + if (!_store[index]!.TryGetTarget(out var target)) { _firstAvailableIndex = Math.Min(_firstAvailableIndex, index); continue; diff --git a/source/Handlebars/Compiler/ClosureBuilder.cs b/source/Handlebars/Compiler/ClosureBuilder.cs index f9a68884..808258bc 100644 --- a/source/Handlebars/Compiler/ClosureBuilder.cs +++ b/source/Handlebars/Compiler/ClosureBuilder.cs @@ -20,72 +20,57 @@ public sealed partial class ClosureBuilder private readonly List>>> _blockHelpers = new(); private readonly List>>> _decorators = new(); private readonly List>>> _blockDecorators = new(); - private readonly List> _other = new(); + private readonly List> _other = new(); public void Add(ConstantExpression constantExpression) { if (constantExpression.Type == typeof(PathInfo)) { - _pathInfos.Add(new KeyValuePair(constantExpression, (PathInfo) constantExpression.Value)); + _pathInfos.Add(new KeyValuePair(constantExpression, (PathInfo) constantExpression.Value!)); } else if (constantExpression.Type == typeof(Ref>)) { - _helpers.Add(new KeyValuePair>>(constantExpression, (Ref>) constantExpression.Value)); + _helpers.Add(new KeyValuePair>>(constantExpression, (Ref>) constantExpression.Value!)); } else if (constantExpression.Type == typeof(Ref>)) { - _blockHelpers.Add(new KeyValuePair>>(constantExpression, (Ref>) constantExpression.Value)); + _blockHelpers.Add(new KeyValuePair>>(constantExpression, (Ref>) constantExpression.Value!)); } else if (constantExpression.Type == typeof(Ref>)) { - _decorators.Add(new KeyValuePair>>(constantExpression, (Ref>) constantExpression.Value)); + _decorators.Add(new KeyValuePair>>(constantExpression, (Ref>) constantExpression.Value!)); } else if (constantExpression.Type == typeof(Ref>)) { - _blockDecorators.Add(new KeyValuePair>>(constantExpression, (Ref>) constantExpression.Value)); + _blockDecorators.Add(new KeyValuePair>>(constantExpression, (Ref>) constantExpression.Value!)); } else if (constantExpression.Type == typeof(TemplateDelegate)) { - _templateDelegates.Add(new KeyValuePair(constantExpression, (TemplateDelegate) constantExpression.Value)); + _templateDelegates.Add(new KeyValuePair(constantExpression, (TemplateDelegate) constantExpression.Value!)); } else if (constantExpression.Type == typeof(DecoratorDelegate)) { - _decoratorDelegates.Add(new KeyValuePair(constantExpression, (DecoratorDelegate) constantExpression.Value)); + _decoratorDelegates.Add(new KeyValuePair(constantExpression, (DecoratorDelegate) constantExpression.Value!)); } else if (constantExpression.Type == typeof(ChainSegment[])) { - _blockParams.Add(new KeyValuePair(constantExpression, (ChainSegment[]) constantExpression.Value)); + _blockParams.Add(new KeyValuePair(constantExpression, (ChainSegment[]) constantExpression.Value!)); } else { - _other.Add(new KeyValuePair(constantExpression, constantExpression.Value)); + _other.Add(new KeyValuePair(constantExpression, constantExpression.Value)); } } public KeyValuePair> Build(out Closure closure) { - var closureType = typeof(Closure); - var constructor = closureType - .GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance) - .Single(); - - var arguments = new List(); - - BuildKnownValues(arguments, _pathInfos, 4); - BuildKnownValues(arguments, _helpers, 4); - BuildKnownValues(arguments, _blockHelpers, 4); - BuildKnownValues(arguments, _templateDelegates, 4); - BuildKnownValues(arguments, _blockParams, 1); - BuildKnownValues(arguments, _decorators, 4); - BuildKnownValues(arguments, _blockDecorators, 4); - BuildKnownValues(arguments, _decoratorDelegates, 4); - arguments.Add(_other.Select(o => o.Value).ToArray()); - - closure = (Closure) constructor.Invoke(arguments.ToArray()); + closure = new Closure(_pathInfos, _helpers, _blockHelpers, _templateDelegates, _blockParams, _decorators, + _blockDecorators, _decoratorDelegates, _other); var mapping = new Dictionary(); - var closureExpression = Expression.Variable(typeof(Closure), "closure"); + var closureType = typeof(Closure); + var closureExpression = Expression.Variable(closureType, "closure"); BuildKnownValuesExpressions(closureExpression, mapping, _pathInfos, "PI", 4); BuildKnownValuesExpressions(closureExpression, mapping, _helpers, "HD", 4); @@ -107,17 +92,6 @@ public KeyValuePair> Bui return new KeyValuePair>(closureExpression, mapping); } - private static void BuildKnownValues(List arguments, List> knowValues, int fieldsCount) - where T: class - { - for (var index = 0; index < fieldsCount; index++) - { - arguments.Add(knowValues.ElementAtOrDefault(index).Value); - } - - arguments.Add(knowValues.Count > fieldsCount ? knowValues.Skip(fieldsCount).Select(o => o.Value).ToArray() : null); - } - private static void BuildKnownValuesExpressions(Expression closure, Dictionary expressions, List> knowValues, string prefix, int fieldsCount) where T: class { @@ -141,93 +115,89 @@ private static void BuildKnownValuesExpressions(Expression closure, Dictionar [SuppressMessage("ReSharper", "InconsistentNaming")] public sealed class Closure { - public readonly PathInfo PI0; - public readonly PathInfo PI1; - public readonly PathInfo PI2; - public readonly PathInfo PI3; - public readonly PathInfo[] PIA; + public readonly PathInfo? PI0; + public readonly PathInfo? PI1; + public readonly PathInfo? PI2; + public readonly PathInfo? PI3; + public readonly PathInfo[]? PIA; - public readonly Ref> HD0; - public readonly Ref> HD1; - public readonly Ref> HD2; - public readonly Ref> HD3; - public readonly Ref>[] HDA; + public readonly Ref>? HD0; + public readonly Ref>? HD1; + public readonly Ref>? HD2; + public readonly Ref>? HD3; + public readonly Ref>[]? HDA; - public readonly Ref> BHD0; - public readonly Ref> BHD1; - public readonly Ref> BHD2; - public readonly Ref> BHD3; - public readonly Ref>[] BHDA; + public readonly Ref>? BHD0; + public readonly Ref>? BHD1; + public readonly Ref>? BHD2; + public readonly Ref>? BHD3; + public readonly Ref>[]? BHDA; - public readonly Ref> DD0; - public readonly Ref> DD1; - public readonly Ref> DD2; - public readonly Ref> DD3; - public readonly Ref>[] DDA; + public readonly Ref>? DD0; + public readonly Ref>? DD1; + public readonly Ref>? DD2; + public readonly Ref>? DD3; + public readonly Ref>[]? DDA; - public readonly Ref> BDD0; - public readonly Ref> BDD1; - public readonly Ref> BDD2; - public readonly Ref> BDD3; - public readonly Ref>[] BDDA; + public readonly Ref>? BDD0; + public readonly Ref>? BDD1; + public readonly Ref>? BDD2; + public readonly Ref>? BDD3; + public readonly Ref>[]? BDDA; - public readonly TemplateDelegate TD0; - public readonly TemplateDelegate TD1; - public readonly TemplateDelegate TD2; - public readonly TemplateDelegate TD3; - public readonly TemplateDelegate[] TDA; + public readonly TemplateDelegate? TD0; + public readonly TemplateDelegate? TD1; + public readonly TemplateDelegate? TD2; + public readonly TemplateDelegate? TD3; + public readonly TemplateDelegate[]? TDA; - public readonly DecoratorDelegate DDD0; - public readonly DecoratorDelegate DDD1; - public readonly DecoratorDelegate DDD2; - public readonly DecoratorDelegate DDD3; - public readonly DecoratorDelegate[] DDDA; + public readonly DecoratorDelegate? DDD0; + public readonly DecoratorDelegate? DDD1; + public readonly DecoratorDelegate? DDD2; + public readonly DecoratorDelegate? DDD3; + public readonly DecoratorDelegate[]? DDDA; - public readonly ChainSegment[] BP0; - public readonly ChainSegment[][] BPA; + public readonly ChainSegment[]? BP0; + public readonly ChainSegment[][]? BPA; - public readonly object[] A; + public readonly object?[] A; + + internal Closure( + List> pathInfos, + List>>> helpers, + List>>> blockHelpers, + List> templateDelegates, + List> blockParams, + List>>> decorators, + List>>> blockDecorators, + List> decoratorDelegates, + List> other) + { + AssignValues(pathInfos, out PI0, out PI1, out PI2, out PI3, out PIA); + AssignValues(helpers, out HD0, out HD1, out HD2, out HD3, out HDA); + AssignValues(blockHelpers, out BHD0, out BHD1, out BHD2, out BHD3, out BHDA); + AssignValues(templateDelegates, out TD0, out TD1, out TD2, out TD3, out TDA); + AssignValues(blockParams, out BP0, out BPA); + AssignValues(decorators, out DD0, out DD1, out DD2, out DD3, out DDA); + AssignValues(blockDecorators, out BDD0, out BDD1, out BDD2, out BDD3, out BDDA); + AssignValues(decoratorDelegates, out DDD0, out DDD1, out DDD2, out DDD3, out DDDA); + A = other.Select(o => o.Value).ToArray(); + } + + private static void AssignValues(List> values, out T? v0, out T[]? a) + { + v0 = values.Count > 0 ? values[0].Value : default; + a = values.Count > 1 ? values.Skip(1).Select(o => o.Value).ToArray() : null; + } - internal Closure(PathInfo pi0, PathInfo pi1, PathInfo pi2, PathInfo pi3, PathInfo[] pia, Ref> hd0, Ref> hd1, Ref> hd2, Ref> hd3, Ref>[] hda, Ref> bhd0, Ref> bhd1, Ref> bhd2, Ref> bhd3, Ref>[] bhda, TemplateDelegate td0, TemplateDelegate td1, TemplateDelegate td2, TemplateDelegate td3, TemplateDelegate[] tda, ChainSegment[] bp0, ChainSegment[][] bpa, Ref> dd0, Ref> dd1, Ref> dd2, Ref> dd3, Ref>[] dda, Ref> bdd0, Ref> bdd1, Ref> bdd2, Ref> bdd3, Ref>[] bdda, DecoratorDelegate ddd0, DecoratorDelegate ddd1, DecoratorDelegate ddd2, DecoratorDelegate ddd3, DecoratorDelegate[] ddda, object[] a) + private static void AssignValues(List> values, out T? v0, out T? v1, + out T? v2, out T? v3, out T[]? a) { - PI0 = pi0; - PI1 = pi1; - PI2 = pi2; - PI3 = pi3; - PIA = pia; - HD0 = hd0; - HD1 = hd1; - HD2 = hd2; - HD3 = hd3; - HDA = hda; - BHD0 = bhd0; - BHD1 = bhd1; - BHD2 = bhd2; - BHD3 = bhd3; - BHDA = bhda; - TD0 = td0; - TD1 = td1; - TD2 = td2; - TD3 = td3; - TDA = tda; - BP0 = bp0; - BPA = bpa; - DD0 = dd0; - DD1 = dd1; - DD2 = dd2; - DD3 = dd3; - DDA = dda; - BDD0 = bdd0; - BDD1 = bdd1; - BDD2 = bdd2; - BDD3 = bdd3; - BDDA = bdda; - DDD0 = ddd0; - DDD1 = ddd1; - DDD2 = ddd2; - DDD3 = ddd3; - DDDA = ddda; - A = a; + v0 = values.Count > 0 ? values[0].Value : default; + v1 = values.Count > 1 ? values[1].Value : default; + v2 = values.Count > 2 ? values[2].Value : default; + v3 = values.Count > 3 ? values[3].Value : default; + a = values.Count > 4 ? values.Skip(4).Select(o => o.Value).ToArray() : null; } } } \ No newline at end of file diff --git a/source/Handlebars/Compiler/FunctionBuilder.cs b/source/Handlebars/Compiler/FunctionBuilder.cs index 15bf96f6..f8f3e5b5 100644 --- a/source/Handlebars/Compiler/FunctionBuilder.cs +++ b/source/Handlebars/Compiler/FunctionBuilder.cs @@ -62,7 +62,7 @@ public static TemplateDelegate Compile(IEnumerable expressions, Comp var expression = CreateExpression(expressions, compilationContext, out decorators); if (expression.Expression is ConstantExpression constantExpression) { - return (TemplateDelegate) constantExpression.Value; + return (TemplateDelegate) constantExpression.Value!; } var lambda = (Expression) expression.Expression; diff --git a/source/Handlebars/Compiler/HandlebarsCompiler.cs b/source/Handlebars/Compiler/HandlebarsCompiler.cs index 0f1be129..398853be 100644 --- a/source/Handlebars/Compiler/HandlebarsCompiler.cs +++ b/source/Handlebars/Compiler/HandlebarsCompiler.cs @@ -72,7 +72,7 @@ internal static TemplateDelegate CompileView(ViewReaderFactory readerFactoryFact if (layoutToken == null) return compiledView; var fs = configuration.FileSystem; - var layoutPath = fs.Closest(templatePath, layoutToken.Value + ".hbs"); + var layoutPath = fs!.Closest(templatePath, layoutToken.Value + ".hbs"); if (layoutPath == null) throw new InvalidOperationException($"Cannot find layout '{layoutToken.Value}' for template '{templatePath}'"); diff --git a/source/Handlebars/Compiler/HandlebarsCompilerException.cs b/source/Handlebars/Compiler/HandlebarsCompilerException.cs index ec0c5f71..c4fe6394 100644 --- a/source/Handlebars/Compiler/HandlebarsCompilerException.cs +++ b/source/Handlebars/Compiler/HandlebarsCompilerException.cs @@ -16,7 +16,7 @@ public HandlebarsCompilerException(string message) { } - internal HandlebarsCompilerException(string message, IReaderContext context = null) + internal HandlebarsCompilerException(string message, IReaderContext? context = null) : this(message, null, context) { } @@ -26,7 +26,7 @@ public HandlebarsCompilerException(string message, Exception innerException) { } - internal HandlebarsCompilerException(string message, Exception innerException, IReaderContext context = null) + internal HandlebarsCompilerException(string message, Exception? innerException, IReaderContext? context = null) : base(message, innerException, context) { } diff --git a/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulator.cs b/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulator.cs index 40068729..757a1c27 100644 --- a/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulator.cs +++ b/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulator.cs @@ -53,7 +53,7 @@ private Expression AccumulateBlock( } else if (context.IsClosingElement(item)) { - return context.GetAccumulatedBlock(); + return context.AccumulatedBlock; } else { diff --git a/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/BlockAccumulatorContext.cs b/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/BlockAccumulatorContext.cs index 345137c0..b5a0a857 100644 --- a/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/BlockAccumulatorContext.cs +++ b/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/BlockAccumulatorContext.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using HandlebarsDotNet.PathStructure; using HandlebarsDotNet.StringUtils; @@ -11,9 +12,9 @@ internal abstract class BlockAccumulatorContext private static readonly HashSet ConditionHelpers = new HashSet(StringComparer.OrdinalIgnoreCase){ "#if", "#unless", "^if", "^unless" }; private static readonly HashSet IteratorHelpers = new HashSet(StringComparer.OrdinalIgnoreCase){ "#each", "^each" }; - public static BlockAccumulatorContext Create(Expression item, Expression parentItem, ICompiledHandlebarsConfiguration configuration) + public static BlockAccumulatorContext? Create(Expression item, Expression? parentItem, ICompiledHandlebarsConfiguration configuration) { - BlockAccumulatorContext context = null; + BlockAccumulatorContext? context = null; if (IsConditionalBlock(item)) { context = new ConditionalBlockAccumulatorContext(item); @@ -77,7 +78,7 @@ private static bool IsPartialBlock (Expression item) }; } - private static bool IsDetachedClosingElement(Expression item, Expression parentItem, out string closingElement) + private static bool IsDetachedClosingElement(Expression item, Expression? parentItem, [MaybeNullWhen(false)] out string closingElement) { closingElement = null; @@ -109,7 +110,7 @@ private static bool IsClosingElementNotMatchOpenElement(string closingElement, s return new Substring(openElement, 1) != new Substring(closingElement, 1); } - private static string GetItemElement(Expression item) + private static string? GetItemElement(Expression? item) { item = UnwrapStatement(item); return item switch @@ -120,7 +121,8 @@ private static string GetItemElement(Expression item) }; } - protected static Expression UnwrapStatement(Expression item) + [return: NotNullIfNotNull(nameof(item))] + protected static Expression? UnwrapStatement(Expression? item) { if (item is StatementExpression expression) { @@ -136,9 +138,10 @@ protected BlockAccumulatorContext(Expression startingNode) public abstract void HandleElement(Expression item); + [MemberNotNullWhen(true, nameof(AccumulatedBlock))] public abstract bool IsClosingElement(Expression item); - public abstract Expression GetAccumulatedBlock(); + public abstract Expression? AccumulatedBlock { get; } } } diff --git a/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/BlockHelperAccumulatorContext.cs b/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/BlockHelperAccumulatorContext.cs index d8f2e3f8..5976721d 100644 --- a/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/BlockHelperAccumulatorContext.cs +++ b/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/BlockHelperAccumulatorContext.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.Compiler { @@ -10,8 +11,8 @@ internal class BlockHelperAccumulatorContext : BlockAccumulatorContext private readonly HelperExpression _startingNode; private readonly bool _trimBefore; private readonly bool _trimAfter; - private Expression _accumulatedBody; - private Expression _accumulatedInversion; + private Expression? _accumulatedBody; + private Expression? _accumulatedInversion; private List _body = new List(); public BlockHelperAccumulatorContext(Expression startingNode) @@ -29,6 +30,7 @@ public BlockHelperAccumulatorContext(Expression startingNode) public sealed override string BlockName { get => _startingNode.HelperName; + [DoesNotReturn] protected set => throw new NotSupportedException(); } @@ -66,36 +68,39 @@ private bool IsClosingNode(Expression item) return item is PathExpression expression && expression.Path == "/" + helperName; } - public override Expression GetAccumulatedBlock() + public override Expression AccumulatedBlock { - if (_accumulatedBody == null) + get { - _accumulatedBody = GetBlockBody(); - _accumulatedInversion = Expression.Block(Expression.Empty()); - } - else if (_accumulatedInversion == null) - { - _accumulatedInversion = GetBlockBody(); - } + if (_accumulatedBody == null) + { + _accumulatedBody = GetBlockBody(); + _accumulatedInversion = Expression.Block(Expression.Empty()); + } + else if (_accumulatedInversion == null) + { + _accumulatedInversion = GetBlockBody(); + } - var resultExpr = HandlebarsExpression.BlockHelper( - _startingNode.HelperName, - _startingNode.Arguments.Where(o => o.NodeType != (ExpressionType)HandlebarsExpressionType.BlockParamsExpression), - _startingNode.Arguments.OfType().SingleOrDefault() ?? BlockParamsExpression.Empty(), - _accumulatedBody, - _accumulatedInversion, - _startingNode.IsRaw); + var resultExpr = HandlebarsExpression.BlockHelper( + _startingNode.HelperName, + _startingNode.Arguments.Where(o => o.NodeType != (ExpressionType)HandlebarsExpressionType.BlockParamsExpression), + _startingNode.Arguments.OfType().SingleOrDefault() ?? BlockParamsExpression.Empty(), + _accumulatedBody, + _accumulatedInversion, + _startingNode.IsRaw); - if (_startingNode.IsRaw) - { - return HandlebarsExpression.Statement( - resultExpr, - false, - _trimBefore, - _trimAfter); - } + if (_startingNode.IsRaw) + { + return HandlebarsExpression.Statement( + resultExpr, + false, + _trimBefore, + _trimAfter); + } - return resultExpr; + return resultExpr; + } } private Expression GetBlockBody() diff --git a/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/ConditionalBlockAccumulatorContext.cs b/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/ConditionalBlockAccumulatorContext.cs index d0b0a973..11794421 100644 --- a/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/ConditionalBlockAccumulatorContext.cs +++ b/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/ConditionalBlockAccumulatorContext.cs @@ -12,7 +12,7 @@ private enum TestType { Direct, Reverse } private static readonly HashSet ValidHelperNames = new HashSet { "if", "unless" }; private readonly List _conditionalBlock = new List(); - private Expression _currentCondition; + private Expression? _currentCondition; private List _bodyBuffer = new List(); public sealed override string BlockName { get; protected set; } @@ -47,7 +47,7 @@ public override void HandleElement(Expression item) { if (IsElseBlock(item)) { - _conditionalBlock.Add(Expression.IfThen(_currentCondition, SinglifyExpressions(_bodyBuffer))); + _conditionalBlock.Add(Expression.IfThen(_currentCondition!, SinglifyExpressions(_bodyBuffer))); if (IsElseIfBlock(item)) { _currentCondition = GetElseIfTestExpression(item); @@ -88,17 +88,20 @@ public override bool IsClosingElement(Expression item) } } - public override Expression GetAccumulatedBlock() + public override Expression? AccumulatedBlock { - ConditionalExpression singleConditional = null; - foreach (var condition in _conditionalBlock.AsEnumerable().Reverse()) + get { - singleConditional = Expression.IfThenElse( - condition.Test, - condition.IfTrue, - (Expression)singleConditional ?? condition.IfFalse); + ConditionalExpression? singleConditional = null; + foreach (var condition in _conditionalBlock.AsEnumerable().Reverse()) + { + singleConditional = Expression.IfThenElse( + condition.Test, + condition.IfTrue, + (Expression?)singleConditional ?? condition.IfFalse); + } + return singleConditional; } - return singleConditional; } private bool IsElseBlock(Expression item) @@ -137,7 +140,7 @@ private static Expression SinglifyExpressions(IEnumerable expression return expressions.SingleOrDefault() ?? Expression.Empty(); } - private static (Expression Value, HashParametersExpression HashParameters) UnwrapBoolishHelperArguments(IEnumerable arguments, IReaderContext context) + private static (Expression Value, HashParametersExpression HashParameters) UnwrapBoolishHelperArguments(IEnumerable arguments, IReaderContext? context) { var value = arguments.First(); if (arguments.Count() == 1) diff --git a/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/IteratorBlockAccumulatorContext.cs b/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/IteratorBlockAccumulatorContext.cs index 0f06a1cd..ed21bce1 100644 --- a/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/IteratorBlockAccumulatorContext.cs +++ b/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/IteratorBlockAccumulatorContext.cs @@ -2,13 +2,14 @@ using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.Compiler { internal class IteratorBlockAccumulatorContext : BlockAccumulatorContext { private readonly HelperExpression _startingNode; - private Expression _accumulatedExpression; + private Expression? _accumulatedExpression; private List _body = new List(); public IteratorBlockAccumulatorContext(Expression startingNode) @@ -21,6 +22,7 @@ public IteratorBlockAccumulatorContext(Expression startingNode) public sealed override string BlockName { get => _startingNode.HelperName; + [DoesNotReturn] protected set => throw new NotSupportedException(); } @@ -55,10 +57,7 @@ public override bool IsClosingElement(Expression item) return true; } - public override Expression GetAccumulatedBlock() - { - return _accumulatedExpression; - } + public override Expression? AccumulatedBlock => _accumulatedExpression; private static bool IsClosingNode(Expression item) { diff --git a/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/PartialBlockAccumulatorContext.cs b/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/PartialBlockAccumulatorContext.cs index c1f30ada..570501ed 100644 --- a/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/PartialBlockAccumulatorContext.cs +++ b/source/Handlebars/Compiler/Lexer/Converter/BlockAccumulators/PartialBlockAccumulatorContext.cs @@ -9,7 +9,7 @@ internal class PartialBlockAccumulatorContext : BlockAccumulatorContext private readonly PartialExpression _startingNode; private readonly List _body = new List(); - public sealed override string BlockName { get; protected set; } + public sealed override string BlockName { get; protected set; } = null!; public PartialBlockAccumulatorContext(Expression startingNode) : base(startingNode) @@ -22,15 +22,18 @@ public override void HandleElement(Expression item) _body.Add(item); } - public override Expression GetAccumulatedBlock() + public override Expression AccumulatedBlock { - var fallback = _body.Count switch + get { - 0 => Expression.Empty(), - 1 => _body[0], - _ => Expression.Block(_body) - }; - return HandlebarsExpression.Partial(_startingNode.PartialName, _startingNode.Argument, fallback); + var fallback = _body.Count switch + { + 0 => Expression.Empty(), + 1 => _body[0], + _ => Expression.Block(_body) + }; + return HandlebarsExpression.Partial(_startingNode.PartialName, _startingNode.Argument, fallback); + } } public override bool IsClosingElement(Expression item) diff --git a/source/Handlebars/Compiler/Lexer/Converter/BlockParamsConverter.cs b/source/Handlebars/Compiler/Lexer/Converter/BlockParamsConverter.cs index 83f3ce6f..52542cf8 100644 --- a/source/Handlebars/Compiler/Lexer/Converter/BlockParamsConverter.cs +++ b/source/Handlebars/Compiler/Lexer/Converter/BlockParamsConverter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using HandlebarsDotNet.Compiler.Lexer; namespace HandlebarsDotNet.Compiler @@ -46,10 +47,10 @@ private static void ConvertBlockParam(BlockParameterToken blockParameterToken, L var pathExpression = result[result.Count - 1] as PathExpression; VerifyBlockParamsSyntax(blockParameterToken, pathExpression); - result[result.Count - 1] = HandlebarsExpression.BlockParams(pathExpression!.Path, blockParameterToken.Value); + result[result.Count - 1] = HandlebarsExpression.BlockParams(pathExpression.Path, blockParameterToken.Value); } - private static void VerifyBlockParamsSyntax(BlockParameterToken blockParameterToken, PathExpression pathExpression) + private static void VerifyBlockParamsSyntax(BlockParameterToken blockParameterToken, [NotNull] PathExpression? pathExpression) { if (pathExpression == null) throw new HandlebarsCompilerException("blockParams definition has incorrect syntax", blockParameterToken.Context); if (!string.Equals("as", pathExpression.Path, StringComparison.OrdinalIgnoreCase)) throw new HandlebarsCompilerException("blockParams definition has incorrect syntax", blockParameterToken.Context); diff --git a/source/Handlebars/Compiler/Lexer/Converter/HelperArgumentAccumulator.cs b/source/Handlebars/Compiler/Lexer/Converter/HelperArgumentAccumulator.cs index 112a19b8..dbc5f99c 100644 --- a/source/Handlebars/Compiler/Lexer/Converter/HelperArgumentAccumulator.cs +++ b/source/Handlebars/Compiler/Lexer/Converter/HelperArgumentAccumulator.cs @@ -34,7 +34,7 @@ public override IEnumerable ConvertTokens(IEnumerable sequence) helper.IsBlock, helperArguments, helper.IsRaw); - yield return enumerator.Current; + yield return enumerator.Current!; break; } case PathExpression path: @@ -46,20 +46,20 @@ public override IEnumerable ConvertTokens(IEnumerable sequence) path.Path, false, helperArguments, - ((EndExpressionToken) enumerator.Current)?.IsRaw ?? false); - yield return enumerator.Current; + ((EndExpressionToken?) enumerator.Current)?.IsRaw ?? false); + yield return enumerator.Current!; } else { yield return path; - yield return enumerator.Current; + yield return enumerator.Current!; } break; } default: - yield return item; + yield return item!; break; } } diff --git a/source/Handlebars/Compiler/Lexer/Converter/HelperConverter.cs b/source/Handlebars/Compiler/Lexer/Converter/HelperConverter.cs index 306c333e..88764084 100644 --- a/source/Handlebars/Compiler/Lexer/Converter/HelperConverter.cs +++ b/source/Handlebars/Compiler/Lexer/Converter/HelperConverter.cs @@ -77,7 +77,7 @@ private bool IsRegisteredHelperName(string name) if (!pathInfo.IsValidHelperLiteral && !_configuration.Compatibility.RelaxedHelperNaming) return false; #pragma warning restore CS0618 if (pathInfo.IsBlockHelper || pathInfo.IsInversion || pathInfo.IsBlockClose || pathInfo.IsThis) return false; - name = pathInfo.TrimmedPath; + name = pathInfo.TrimmedPath!; return _configuration.Helpers.ContainsKey(pathInfo) || _configuration.Decorators.ContainsKey(pathInfo) || BuiltInHelpers.Contains(name); } @@ -92,7 +92,7 @@ private bool IsRegisteredBlockHelperName(string name, bool isRaw) if (pathInfo.IsBlockClose) return false; if (pathInfo.IsThis) return false; - name = pathInfo.TrimmedPath; + name = pathInfo.TrimmedPath!; return _configuration.BlockHelpers.ContainsKey(pathInfo) || _configuration.BlockDecorators.ContainsKey(pathInfo) || BuiltInHelpers.Contains(name); } diff --git a/source/Handlebars/Compiler/Lexer/Converter/RawHelperAccumulator.cs b/source/Handlebars/Compiler/Lexer/Converter/RawHelperAccumulator.cs index 07f57a00..c773376f 100644 --- a/source/Handlebars/Compiler/Lexer/Converter/RawHelperAccumulator.cs +++ b/source/Handlebars/Compiler/Lexer/Converter/RawHelperAccumulator.cs @@ -81,7 +81,7 @@ private IEnumerable CollectBody(IEnumerator enumerator, string r using (var container = StringBuilderPool.Shared.Use()) { var buffer = container.Value; - object precedingItem = null; + object? precedingItem = null; while (enumerator.MoveNext()) { @@ -120,7 +120,7 @@ private bool IsClosingTag(StartExpressionToken startExpressionToken, object item && word.Value == ("/" + helperName); } - private static string Stringify(object item, object precedingItem) + private static string Stringify(object item, object? precedingItem) { if (item is Token token) { @@ -132,10 +132,10 @@ private static string Stringify(object item, object precedingItem) return helperExpression.HelperName; } - return item.ToString(); + return item.ToString()!; } - private static string PrependWhitespaceWhereNeeded(string value, Token currToken, object precedingItem) + private static string PrependWhitespaceWhereNeeded(string value, Token currToken, object? precedingItem) { if (precedingItem == null) { diff --git a/source/Handlebars/Compiler/Lexer/Converter/WhitespaceRemover.cs b/source/Handlebars/Compiler/Lexer/Converter/WhitespaceRemover.cs index b36f67ab..5805fe58 100644 --- a/source/Handlebars/Compiler/Lexer/Converter/WhitespaceRemover.cs +++ b/source/Handlebars/Compiler/Lexer/Converter/WhitespaceRemover.cs @@ -195,12 +195,12 @@ private static bool IsBlockStatement(StatementExpression statement) IsSectionOrClosingNode(statement.Body as PathExpression); } - private static bool IsSectionOrClosingNode(PathExpression pathExpression) + private static bool IsSectionOrClosingNode(PathExpression? pathExpression) { return (pathExpression != null) && pathExpression.Path.IndexOfAny(new[] {'#', '/', '^'}) == 0; } - private static bool IsBlockHelperOrInversion(HelperExpression helperExpression) + private static bool IsBlockHelperOrInversion(HelperExpression? helperExpression) { if (helperExpression == null) return false; diff --git a/source/Handlebars/Compiler/Lexer/HandlebarsParserException.cs b/source/Handlebars/Compiler/Lexer/HandlebarsParserException.cs index d56a4112..edf58dce 100644 --- a/source/Handlebars/Compiler/Lexer/HandlebarsParserException.cs +++ b/source/Handlebars/Compiler/Lexer/HandlebarsParserException.cs @@ -12,7 +12,7 @@ public HandlebarsParserException(string message) { } - internal HandlebarsParserException(string message, IReaderContext context = null) + internal HandlebarsParserException(string message, IReaderContext? context = null) : this(message, null, context) { } @@ -22,7 +22,7 @@ public HandlebarsParserException(string message, Exception innerException) { } - internal HandlebarsParserException(string message, Exception innerException, IReaderContext context = null) + internal HandlebarsParserException(string message, Exception? innerException, IReaderContext? context = null) : base(message, innerException, context) { } diff --git a/source/Handlebars/Compiler/Lexer/Parsers/BlockParamsParser.cs b/source/Handlebars/Compiler/Lexer/Parsers/BlockParamsParser.cs index aedc18f0..02dec468 100644 --- a/source/Handlebars/Compiler/Lexer/Parsers/BlockParamsParser.cs +++ b/source/Handlebars/Compiler/Lexer/Parsers/BlockParamsParser.cs @@ -4,16 +4,16 @@ namespace HandlebarsDotNet.Compiler.Lexer { internal class BlockParamsParser : Parser { - public override Token Parse(ExtendedStringReader reader) + public override Token? Parse(ExtendedStringReader reader) { var context = reader.GetContext(); var buffer = AccumulateWord(reader); - return !string.IsNullOrEmpty(buffer) + return buffer is {Length: > 0} ? Token.BlockParams(buffer, context) : null; } - private static string AccumulateWord(ExtendedStringReader reader) + private static string? AccumulateWord(ExtendedStringReader reader) { using(var container = StringBuilderPool.Shared.Use()) { diff --git a/source/Handlebars/Compiler/Lexer/Parsers/BlockWordParser.cs b/source/Handlebars/Compiler/Lexer/Parsers/BlockWordParser.cs index 9a7a1e0c..585a91d6 100644 --- a/source/Handlebars/Compiler/Lexer/Parsers/BlockWordParser.cs +++ b/source/Handlebars/Compiler/Lexer/Parsers/BlockWordParser.cs @@ -10,7 +10,7 @@ internal class BlockWordParser : Parser '#', '^', '/' }; - public override Token Parse(ExtendedStringReader reader) + public override Token? Parse(ExtendedStringReader reader) { if (!IsBlockWord(reader)) return null; diff --git a/source/Handlebars/Compiler/Lexer/Parsers/CommentParser.cs b/source/Handlebars/Compiler/Lexer/Parsers/CommentParser.cs index 1667fdcd..8ac8d8bd 100644 --- a/source/Handlebars/Compiler/Lexer/Parsers/CommentParser.cs +++ b/source/Handlebars/Compiler/Lexer/Parsers/CommentParser.cs @@ -5,11 +5,11 @@ namespace HandlebarsDotNet.Compiler.Lexer { internal class CommentParser : Parser { - public override Token Parse(ExtendedStringReader reader) + public override Token? Parse(ExtendedStringReader reader) { if (!IsComment(reader)) return null; - Token token = null; + Token? token = null; var buffer = AccumulateComment(reader, out bool isEscaped).Trim(); if (buffer.StartsWith("<") && !isEscaped) //syntax for layout is {{!< layoutname }} - i.e. its inside a comment block { diff --git a/source/Handlebars/Compiler/Lexer/Parsers/LiteralParser.cs b/source/Handlebars/Compiler/Lexer/Parsers/LiteralParser.cs index e4b1a340..d7bf6331 100644 --- a/source/Handlebars/Compiler/Lexer/Parsers/LiteralParser.cs +++ b/source/Handlebars/Compiler/Lexer/Parsers/LiteralParser.cs @@ -5,7 +5,7 @@ namespace HandlebarsDotNet.Compiler.Lexer { internal class LiteralParser : Parser { - public override Token Parse(ExtendedStringReader reader) + public override Token? Parse(ExtendedStringReader reader) { var context = reader.GetContext(); if (IsDelimitedLiteral(reader)) diff --git a/source/Handlebars/Compiler/Lexer/Parsers/Parser.cs b/source/Handlebars/Compiler/Lexer/Parsers/Parser.cs index 40607e30..f1c4e66e 100644 --- a/source/Handlebars/Compiler/Lexer/Parsers/Parser.cs +++ b/source/Handlebars/Compiler/Lexer/Parsers/Parser.cs @@ -2,7 +2,7 @@ { internal abstract class Parser { - public abstract Token Parse(ExtendedStringReader reader); + public abstract Token? Parse(ExtendedStringReader reader); } } diff --git a/source/Handlebars/Compiler/Lexer/Parsers/PartialParser.cs b/source/Handlebars/Compiler/Lexer/Parsers/PartialParser.cs index 15a0881e..d6ae62f9 100644 --- a/source/Handlebars/Compiler/Lexer/Parsers/PartialParser.cs +++ b/source/Handlebars/Compiler/Lexer/Parsers/PartialParser.cs @@ -2,9 +2,9 @@ { internal class PartialParser : Parser { - public override Token Parse(ExtendedStringReader reader) + public override Token? Parse(ExtendedStringReader reader) { - PartialToken token = null; + PartialToken? token = null; if ((char)reader.Peek() == '>') { token = Token.Partial(reader.GetContext()); diff --git a/source/Handlebars/Compiler/Lexer/Parsers/WordParser.cs b/source/Handlebars/Compiler/Lexer/Parsers/WordParser.cs index 7463f330..371ae48f 100644 --- a/source/Handlebars/Compiler/Lexer/Parsers/WordParser.cs +++ b/source/Handlebars/Compiler/Lexer/Parsers/WordParser.cs @@ -33,7 +33,7 @@ static WordParser() } } - public override Token Parse(ExtendedStringReader reader) + public override Token? Parse(ExtendedStringReader reader) { var context = reader.GetContext(); if (!IsWord(reader)) return null; diff --git a/source/Handlebars/Compiler/Lexer/Tokens/BlockParameterToken.cs b/source/Handlebars/Compiler/Lexer/Tokens/BlockParameterToken.cs index 05bd0a52..28fe4d58 100644 --- a/source/Handlebars/Compiler/Lexer/Tokens/BlockParameterToken.cs +++ b/source/Handlebars/Compiler/Lexer/Tokens/BlockParameterToken.cs @@ -2,7 +2,7 @@ namespace HandlebarsDotNet.Compiler.Lexer { internal class BlockParameterToken : Token { - public BlockParameterToken(string value, IReaderContext context = null) + public BlockParameterToken(string value, IReaderContext? context = null) { Value = value; Context = context; @@ -11,6 +11,6 @@ public BlockParameterToken(string value, IReaderContext context = null) public override TokenType Type => TokenType.BlockParams; public override string Value { get; } - public IReaderContext Context { get; } + public IReaderContext? Context { get; } } } \ No newline at end of file diff --git a/source/Handlebars/Compiler/Lexer/Tokens/EndExpressionToken.cs b/source/Handlebars/Compiler/Lexer/Tokens/EndExpressionToken.cs index 1d214bb0..35cd3452 100644 --- a/source/Handlebars/Compiler/Lexer/Tokens/EndExpressionToken.cs +++ b/source/Handlebars/Compiler/Lexer/Tokens/EndExpressionToken.cs @@ -2,7 +2,7 @@ { internal class EndExpressionToken : ExpressionScopeToken { - public EndExpressionToken(bool isEscaped, bool trimWhitespace, bool isRaw, IReaderContext context) + public EndExpressionToken(bool isEscaped, bool trimWhitespace, bool isRaw, IReaderContext? context) { IsEscaped = isEscaped; TrimTrailingWhitespace = trimWhitespace; @@ -15,7 +15,7 @@ public EndExpressionToken(bool isEscaped, bool trimWhitespace, bool isRaw, IRead public bool TrimTrailingWhitespace { get; } public bool IsRaw { get; } - public IReaderContext Context { get; } + public IReaderContext? Context { get; } public override string Value { diff --git a/source/Handlebars/Compiler/Lexer/Tokens/LiteralExpressionToken.cs b/source/Handlebars/Compiler/Lexer/Tokens/LiteralExpressionToken.cs index 365849ca..48129b80 100644 --- a/source/Handlebars/Compiler/Lexer/Tokens/LiteralExpressionToken.cs +++ b/source/Handlebars/Compiler/Lexer/Tokens/LiteralExpressionToken.cs @@ -1,19 +1,22 @@ -namespace HandlebarsDotNet.Compiler.Lexer +using System.Diagnostics.CodeAnalysis; + +namespace HandlebarsDotNet.Compiler.Lexer { internal class LiteralExpressionToken : ExpressionToken { - public LiteralExpressionToken(string value, string delimiter = null, IReaderContext context = null) + public LiteralExpressionToken(string value, string? delimiter = null, IReaderContext? context = null) { Context = context; Value = value; Delimiter = delimiter; } - public IReaderContext Context { get; } + public IReaderContext? Context { get; } + [MemberNotNullWhen(true, nameof(Delimiter))] public bool IsDelimitedLiteral => Delimiter != null; - public string Delimiter { get; } + public string? Delimiter { get; } public override TokenType Type => TokenType.Literal; diff --git a/source/Handlebars/Compiler/Lexer/Tokens/PartialToken.cs b/source/Handlebars/Compiler/Lexer/Tokens/PartialToken.cs index 1c7a1055..0eeaaa90 100644 --- a/source/Handlebars/Compiler/Lexer/Tokens/PartialToken.cs +++ b/source/Handlebars/Compiler/Lexer/Tokens/PartialToken.cs @@ -2,12 +2,12 @@ { internal class PartialToken : Token { - public PartialToken(IReaderContext context = null) + public PartialToken(IReaderContext? context = null) { Context = context; } - public IReaderContext Context { get; } + public IReaderContext? Context { get; } public override TokenType Type => TokenType.Partial; diff --git a/source/Handlebars/Compiler/Lexer/Tokens/StartExpressionToken.cs b/source/Handlebars/Compiler/Lexer/Tokens/StartExpressionToken.cs index 41e7f87a..2d75385d 100644 --- a/source/Handlebars/Compiler/Lexer/Tokens/StartExpressionToken.cs +++ b/source/Handlebars/Compiler/Lexer/Tokens/StartExpressionToken.cs @@ -2,7 +2,7 @@ { internal class StartExpressionToken : ExpressionScopeToken { - public StartExpressionToken(bool isEscaped, bool trimWhitespace, bool isRaw, IReaderContext context) + public StartExpressionToken(bool isEscaped, bool trimWhitespace, bool isRaw, IReaderContext? context) { Context = context; IsEscaped = isEscaped; @@ -10,7 +10,7 @@ public StartExpressionToken(bool isEscaped, bool trimWhitespace, bool isRaw, IRe IsRaw = isRaw; } - public IReaderContext Context { get; } + public IReaderContext? Context { get; } public bool IsEscaped { get; } diff --git a/source/Handlebars/Compiler/Lexer/Tokens/StaticToken.cs b/source/Handlebars/Compiler/Lexer/Tokens/StaticToken.cs index 820d37a7..4ab1887e 100644 --- a/source/Handlebars/Compiler/Lexer/Tokens/StaticToken.cs +++ b/source/Handlebars/Compiler/Lexer/Tokens/StaticToken.cs @@ -2,16 +2,16 @@ { internal class StaticToken : Token { - public IReaderContext Context { get; } + public IReaderContext? Context { get; } - private StaticToken(string value, string original, IReaderContext context = null) + private StaticToken(string value, string original, IReaderContext? context = null) { Value = value; Original = original; Context = context; } - internal StaticToken(string value, IReaderContext context = null) + internal StaticToken(string value, IReaderContext? context = null) : this(value, value) { Context = context; diff --git a/source/Handlebars/Compiler/Lexer/Tokens/Token.cs b/source/Handlebars/Compiler/Lexer/Tokens/Token.cs index 993e7252..5debda65 100644 --- a/source/Handlebars/Compiler/Lexer/Tokens/Token.cs +++ b/source/Handlebars/Compiler/Lexer/Tokens/Token.cs @@ -11,27 +11,27 @@ public sealed override string ToString() return Value; } - public static StaticToken Static(string value, IReaderContext context = null) + public static StaticToken Static(string value, IReaderContext? context = null) { return new StaticToken(value, context); } - public static LiteralExpressionToken Literal(string value, string delimiter = null, IReaderContext context = null) + public static LiteralExpressionToken Literal(string value, string? delimiter = null, IReaderContext? context = null) { return new LiteralExpressionToken(value, delimiter, context); } - public static WordExpressionToken Word(string word, IReaderContext context = null) + public static WordExpressionToken Word(string word, IReaderContext? context = null) { return new WordExpressionToken(word, context); } - public static StartExpressionToken StartExpression(bool isEscaped, bool trimWhitespace, bool isRaw, IReaderContext context = null) + public static StartExpressionToken StartExpression(bool isEscaped, bool trimWhitespace, bool isRaw, IReaderContext? context = null) { return new StartExpressionToken(isEscaped, trimWhitespace, isRaw, context); } - public static EndExpressionToken EndExpression(bool isEscaped, bool trimWhitespace, bool isRaw, IReaderContext context = null) + public static EndExpressionToken EndExpression(bool isEscaped, bool trimWhitespace, bool isRaw, IReaderContext? context = null) { return new EndExpressionToken(isEscaped, trimWhitespace, isRaw, context); } @@ -41,7 +41,7 @@ public static CommentToken Comment(string comment) return new CommentToken(comment); } - public static PartialToken Partial(IReaderContext context = null) + public static PartialToken Partial(IReaderContext? context = null) { return new PartialToken(context); } diff --git a/source/Handlebars/Compiler/Lexer/Tokens/WordExpressionToken.cs b/source/Handlebars/Compiler/Lexer/Tokens/WordExpressionToken.cs index 1c64ec41..0fd86c8f 100644 --- a/source/Handlebars/Compiler/Lexer/Tokens/WordExpressionToken.cs +++ b/source/Handlebars/Compiler/Lexer/Tokens/WordExpressionToken.cs @@ -2,7 +2,7 @@ { internal class WordExpressionToken : ExpressionToken { - public WordExpressionToken(string word, IReaderContext context = null) + public WordExpressionToken(string word, IReaderContext? context = null) { Value = word; Context = context; @@ -11,7 +11,7 @@ public WordExpressionToken(string word, IReaderContext context = null) public override TokenType Type => TokenType.Word; public override string Value { get; } - public IReaderContext Context { get; } + public IReaderContext? Context { get; } } } diff --git a/source/Handlebars/Compiler/Structure/BlockParamsExpression.cs b/source/Handlebars/Compiler/Structure/BlockParamsExpression.cs index 7acd3238..3727e601 100644 --- a/source/Handlebars/Compiler/Structure/BlockParamsExpression.cs +++ b/source/Handlebars/Compiler/Structure/BlockParamsExpression.cs @@ -9,21 +9,20 @@ internal class BlockParamsExpression : HandlebarsExpression { public new static BlockParamsExpression Empty() => new BlockParamsExpression(null); - public readonly BlockParam BlockParam; + public readonly BlockParam? BlockParam; - private BlockParamsExpression(BlockParam blockParam) + private BlockParamsExpression(BlockParam? blockParam) { BlockParam = blockParam; } - + public BlockParamsExpression(string action, string blockParams) - :this(new BlockParam - { - Action = action, - Parameters = blockParams.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries) + : this(new BlockParam( + action, + blockParams.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries) .Select(ChainSegment.Create) .ToArray() - }) + )) { } @@ -41,5 +40,11 @@ internal class BlockParam { public string Action { get; set; } public ChainSegment[] Parameters { get; set; } + + public BlockParam(string action, ChainSegment[] parameters) + { + Action = action; + Parameters = parameters; + } } } \ No newline at end of file diff --git a/source/Handlebars/Compiler/Structure/CommentExpression.cs b/source/Handlebars/Compiler/Structure/CommentExpression.cs index c687843a..8c1a9219 100644 --- a/source/Handlebars/Compiler/Structure/CommentExpression.cs +++ b/source/Handlebars/Compiler/Structure/CommentExpression.cs @@ -4,12 +4,12 @@ namespace HandlebarsDotNet.Compiler { internal class CommentExpression : HandlebarsExpression { - public CommentExpression(string value) + public CommentExpression(string? value) { Value = value; } - public string Value { get; } + public string? Value { get; } public override ExpressionType NodeType => (ExpressionType) HandlebarsExpressionType.CommentExpression; } diff --git a/source/Handlebars/Compiler/Structure/HandlebarsExpression.cs b/source/Handlebars/Compiler/Structure/HandlebarsExpression.cs index 73794e70..c1a0be67 100644 --- a/source/Handlebars/Compiler/Structure/HandlebarsExpression.cs +++ b/source/Handlebars/Compiler/Structure/HandlebarsExpression.cs @@ -32,7 +32,7 @@ public static HelperExpression Helper(string helperName, bool isBlock, IEnumerab return new HelperExpression(helperName, isBlock, arguments, isRaw); } - public static HelperExpression Helper(string helperName, bool isBlock, bool isRaw = false, IReaderContext context = null) + public static HelperExpression Helper(string helperName, bool isBlock, bool isRaw = false, IReaderContext? context = null) { return new HelperExpression(helperName, isBlock, isRaw, context); } @@ -92,17 +92,17 @@ public static PartialExpression Partial(Expression partialName) return Partial(partialName, null); } - public static PartialExpression Partial(Expression partialName, Expression argument) + public static PartialExpression Partial(Expression partialName, Expression? argument) { return new PartialExpression(partialName, argument, null, false); } - public static PartialExpression Partial(Expression partialName, Expression argument, Expression fallback) + public static PartialExpression Partial(Expression partialName, Expression? argument, Expression fallback) { return new PartialExpression(partialName, argument, fallback, true); } - public static PartialExpression Partial(Expression partialName, Expression argument, Expression fallback, bool isBlock, string indent) + public static PartialExpression Partial(Expression partialName, Expression? argument, Expression? fallback, bool isBlock, string? indent) { return new PartialExpression(partialName, argument, fallback, isBlock, indent); } @@ -127,7 +127,7 @@ public static HashParametersExpression HashParametersExpression(Dictionary arguments, bool isRaw = false, IReaderContext context = null) + public HelperExpression(string helperName, bool isBlock, IEnumerable arguments, bool isRaw = false, IReaderContext? context = null) : this(helperName, isBlock, isRaw) { Arguments = arguments; @@ -15,7 +15,7 @@ public HelperExpression(string helperName, bool isBlock, IEnumerable IsBlock = isBlock; } - public HelperExpression(string helperName, bool isBlock, bool isRaw = false, IReaderContext context = null) + public HelperExpression(string helperName, bool isBlock, bool isRaw = false, IReaderContext? context = null) { HelperName = helperName; IsRaw = isRaw; @@ -35,7 +35,7 @@ public HelperExpression(string helperName, bool isBlock, bool isRaw = false, IRe public bool IsBlock { get; set; } public IEnumerable Arguments { get; } - public IReaderContext Context { get; } + public IReaderContext? Context { get; } } } diff --git a/source/Handlebars/Compiler/Structure/PartialExpression.cs b/source/Handlebars/Compiler/Structure/PartialExpression.cs index 0b3ef979..1e2a28d6 100644 --- a/source/Handlebars/Compiler/Structure/PartialExpression.cs +++ b/source/Handlebars/Compiler/Structure/PartialExpression.cs @@ -4,7 +4,7 @@ namespace HandlebarsDotNet.Compiler { internal class PartialExpression : HandlebarsExpression { - public PartialExpression(Expression partialName, Expression argument, Expression fallback, bool isBlock = false, string indent = null) + public PartialExpression(Expression partialName, Expression? argument, Expression? fallback, bool isBlock = false, string? indent = null) { PartialName = partialName; Argument = argument; @@ -17,9 +17,9 @@ public PartialExpression(Expression partialName, Expression argument, Expression public Expression PartialName { get; } - public Expression Argument { get; } + public Expression? Argument { get; } - public Expression Fallback { get; } + public Expression? Fallback { get; } public bool IsBlock { get; } @@ -28,7 +28,7 @@ public PartialExpression(Expression partialName, Expression argument, Expression /// When non-null/non-empty, this indentation is prepended to every line of the rendered partial output, /// matching Handlebars.js standalone partial indentation behavior. /// - public string Indent { get; } + public string? Indent { get; } } } diff --git a/source/Handlebars/Compiler/Translation/Expression/BlockHelperFunctionBinder.cs b/source/Handlebars/Compiler/Translation/Expression/BlockHelperFunctionBinder.cs index 97c27d80..8611597b 100644 --- a/source/Handlebars/Compiler/Translation/Expression/BlockHelperFunctionBinder.cs +++ b/source/Handlebars/Compiler/Translation/Expression/BlockHelperFunctionBinder.cs @@ -34,7 +34,7 @@ protected override Expression VisitStatementExpression(StatementExpression sex) protected override Expression VisitBlockHelperExpression(BlockHelperExpression bhex) { - var pathInfo = PathInfoStore.Current.GetOrAdd(bhex.HelperName); + var pathInfo = PathInfoStore.Current!.GetOrAdd(bhex.HelperName); var bindingContext = CompilationContext.Args.BindingContext; var direction = bhex.IsRaw || pathInfo.IsBlockHelper ? BlockHelperDirection.Direct : BlockHelperDirection.Inverse; @@ -172,7 +172,7 @@ out _ private IEnumerable VisitDecoratorBlockExpression(BlockHelperExpression bhex) { - var pathInfo = PathInfoStore.Current.GetOrAdd(bhex.HelperName); + var pathInfo = PathInfoStore.Current!.GetOrAdd(bhex.HelperName); var bindingContext = CompilationContext.Args.BindingContext; var direction = bhex.IsRaw || pathInfo.IsBlockHelper ? BlockHelperDirection.Direct : BlockHelperDirection.Inverse; diff --git a/source/Handlebars/Compiler/Translation/Expression/DecoratorDefinition.cs b/source/Handlebars/Compiler/Translation/Expression/DecoratorDefinition.cs index 85c2f28a..50343553 100644 --- a/source/Handlebars/Compiler/Translation/Expression/DecoratorDefinition.cs +++ b/source/Handlebars/Compiler/Translation/Expression/DecoratorDefinition.cs @@ -26,7 +26,7 @@ public DecoratorDelegate Compile(CompilationContext context) Decorator, context.EncodedWriter, context.BindingContext, - Function.Expression as ParameterExpression + (Function.Expression as ParameterExpression)! ); return context.Configuration.ExpressionCompiler.Compile(lambda); diff --git a/source/Handlebars/Compiler/Translation/Expression/FunctionBinderHelpers.cs b/source/Handlebars/Compiler/Translation/Expression/FunctionBinderHelpers.cs index fb35f1c8..ab20ac55 100644 --- a/source/Handlebars/Compiler/Translation/Expression/FunctionBinderHelpers.cs +++ b/source/Handlebars/Compiler/Translation/Expression/FunctionBinderHelpers.cs @@ -27,7 +27,7 @@ private static readonly LookupSlim, I argumentTypes[index] = objectType; } - return typeof(Arguments).GetConstructor(argumentTypes); + return typeof(Arguments).GetConstructor(argumentTypes)!; }); }; diff --git a/source/Handlebars/Compiler/Translation/Expression/HandlebarsExpressionVisitor.cs b/source/Handlebars/Compiler/Translation/Expression/HandlebarsExpressionVisitor.cs index 94434867..b794d924 100644 --- a/source/Handlebars/Compiler/Translation/Expression/HandlebarsExpressionVisitor.cs +++ b/source/Handlebars/Compiler/Translation/Expression/HandlebarsExpressionVisitor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; @@ -7,7 +8,8 @@ namespace HandlebarsDotNet.Compiler { internal class HandlebarsExpressionVisitor : ExpressionVisitor { - public override Expression Visit(Expression node) + [return: NotNullIfNotNull(nameof(node))] + public override Expression? Visit(Expression? node) { if (node == null) { @@ -99,7 +101,7 @@ protected virtual Expression VisitIteratorExpression(IteratorExpression iex) protected virtual Expression VisitPartialExpression(PartialExpression pex) { Expression partialName = Visit(pex.PartialName); - Expression argument = Visit(pex.Argument); + Expression? argument = Visit(pex.Argument); // Don't visit Fallback - it will be compiled separately if (partialName != pex.PartialName @@ -151,12 +153,13 @@ protected virtual Expression VisitHashParametersExpression(HashParametersExpress return hpex; } - private IEnumerable VisitExpressionList(IEnumerable original) + [return: NotNullIfNotNull(nameof(original))] + private IEnumerable? VisitExpressionList(IEnumerable? original) { if (original == null) return Array.Empty(); var originalAsList = original as IReadOnlyList ?? original.ToArray(); - List list = null; + List? list = null; for (int index = 0; index < originalAsList.Count; index++) { var p = Visit(originalAsList[index]); diff --git a/source/Handlebars/Compiler/Translation/Expression/HashParameterBinder.cs b/source/Handlebars/Compiler/Translation/Expression/HashParameterBinder.cs index 09a4f92f..5d526552 100644 --- a/source/Handlebars/Compiler/Translation/Expression/HashParameterBinder.cs +++ b/source/Handlebars/Compiler/Translation/Expression/HashParameterBinder.cs @@ -7,23 +7,25 @@ namespace HandlebarsDotNet.Compiler { internal class HashParameterBinder : HandlebarsExpressionVisitor { + private static readonly NewExpression NewExpression = + (NewExpression) ((Expression>) (() => new HashParameterDictionary())).Body; + + private static readonly MethodInfo AddMethod = + new Action(new HashParameterDictionary().Add).GetMethodInfo(); + protected override Expression VisitHashParametersExpression(HashParametersExpression hpex) { - var addMethod = typeof(HashParameterDictionary).GetMethod("Add", new[] { typeof(string), typeof(object) }); - var elementInits = new List(); foreach (var parameter in hpex.Parameters) { elementInits.Add(Expression.ElementInit( - addMethod, + AddMethod, Expression.Constant(parameter.Key), Visit(parameter.Value))); } - return Expression.ListInit( - Expression.New(typeof(HashParameterDictionary).GetConstructor(new Type[0])), - elementInits); + return Expression.ListInit(NewExpression, elementInits); } } } diff --git a/source/Handlebars/Compiler/Translation/Expression/HashParameterDictionary.cs b/source/Handlebars/Compiler/Translation/Expression/HashParameterDictionary.cs index 223cee02..febc9e80 100644 --- a/source/Handlebars/Compiler/Translation/Expression/HashParameterDictionary.cs +++ b/source/Handlebars/Compiler/Translation/Expression/HashParameterDictionary.cs @@ -3,9 +3,9 @@ namespace HandlebarsDotNet.Compiler { - public class HashParameterDictionary : Dictionary + public class HashParameterDictionary : Dictionary { - internal static readonly IReadOnlyDictionary Empty = new HashParameterDictionary(); + internal static readonly IReadOnlyDictionary Empty = new HashParameterDictionary(); public HashParameterDictionary() :base(StringComparer.OrdinalIgnoreCase) diff --git a/source/Handlebars/Compiler/Translation/Expression/HelperFunctionBinder.cs b/source/Handlebars/Compiler/Translation/Expression/HelperFunctionBinder.cs index 41e9f402..ea17cfb3 100644 --- a/source/Handlebars/Compiler/Translation/Expression/HelperFunctionBinder.cs +++ b/source/Handlebars/Compiler/Translation/Expression/HelperFunctionBinder.cs @@ -34,7 +34,7 @@ protected override Expression VisitHelperExpression(HelperExpression hex) return Expression.Empty(); } - var pathInfo = PathInfoStore.Current.GetOrAdd(hex.HelperName); + var pathInfo = PathInfoStore.Current!.GetOrAdd(hex.HelperName); #pragma warning disable CS0618 if(!pathInfo.IsValidHelperLiteral && !CompilationContext.Configuration.Compatibility.RelaxedHelperNaming) return Expression.Empty(); #pragma warning restore CS0618 @@ -71,7 +71,7 @@ protected override Expression VisitHelperExpression(HelperExpression hex) private DecoratorDefinition VisitDecoratorExpression(HelperExpression hex) { - var pathInfo = PathInfoStore.Current.GetOrAdd(hex.HelperName); + var pathInfo = PathInfoStore.Current!.GetOrAdd(hex.HelperName); #pragma warning disable CS0618 if(!pathInfo.IsValidHelperLiteral && !CompilationContext.Configuration.Compatibility.RelaxedHelperNaming) return new DecoratorDefinition(); #pragma warning restore CS0618 diff --git a/source/Handlebars/Compiler/Translation/Expression/PartialBinder.cs b/source/Handlebars/Compiler/Translation/Expression/PartialBinder.cs index 4016ad04..a94152fe 100644 --- a/source/Handlebars/Compiler/Translation/Expression/PartialBinder.cs +++ b/source/Handlebars/Compiler/Translation/Expression/PartialBinder.cs @@ -48,7 +48,7 @@ protected override Expression VisitPartialExpression(PartialExpression pex) if (pex.Argument != null || partialBlockTemplate != null) { var value = pex.Argument != null - ? Arg(FunctionBuilder.Reduce(pex.Argument, CompilationContext, out _)) + ? Arg(FunctionBuilder.Reduce(pex.Argument, CompilationContext, out _)) : bindingContext.Property(o => o.Value); var partialTemplate = Arg(partialBlockTemplate); @@ -83,7 +83,7 @@ out _ if (pex.Argument != null || partialBlockTemplate != null) { var value = pex.Argument != null - ? Arg(FunctionBuilder.Reduce(pex.Argument, CompilationContext, out _)) + ? Arg(FunctionBuilder.Reduce(pex.Argument, CompilationContext, out _)) : bindingContext.Property(o => o.Value); var partialTemplate = Arg(partialBlockTemplate); @@ -108,9 +108,9 @@ private static void InvokePartialWithFallback( EncodedTextWriter writer, ICompiledHandlebarsConfiguration configuration, bool block, - string indent) + string? indent) { - partialName = partialName != null ? ChainSegment.Create(partialName).TrimmedValue : null; + partialName = ChainSegment.Create(partialName).TrimmedValue; if (InvokePartial(partialName, context, writer, configuration, block, indent)) return; if (context.PartialBlockTemplate == null) { @@ -142,7 +142,7 @@ private static void InvokePartialWithFallback( /// Newlines are normalised to \n so that output is consistent across platforms regardless /// of whether the partial source was checked out with \r\n line endings. /// - private static void WriteWithIndent(EncodedTextWriter writer, string content, string indent) + private static void WriteWithIndent(EncodedTextWriter writer, string? content, string? indent) { if (string.IsNullOrEmpty(content)) { @@ -177,7 +177,7 @@ private static bool InvokePartial( EncodedTextWriter writer, ICompiledHandlebarsConfiguration configuration, bool block, - string indent) + string? indent) { if (partialName.Equals(SpecialPartialBlockName)) { @@ -186,7 +186,7 @@ private static bool InvokePartial( // If we are a block, our contents are the fallback and SpecialPartialBlockName refers to our parent if (block) { - partialBlockTemplate = context.ParentContext.PartialBlockTemplate; + partialBlockTemplate = context.ParentContext!.PartialBlockTemplate; } if (partialBlockTemplate == null) @@ -196,7 +196,7 @@ private static bool InvokePartial( try { - context.PartialBlockTemplate = context.ParentContext.PartialBlockTemplate; + context.PartialBlockTemplate = context.ParentContext!.PartialBlockTemplate; if (!string.IsNullOrEmpty(indent)) { using var innerWriter = ReusableStringWriter.Get(writer.UnderlyingWriter.FormatProvider); @@ -252,7 +252,7 @@ void IncreaseDepth() { var handlebars = Handlebars.Create(configuration); if (configuration.PartialTemplateResolver == null - || !configuration.PartialTemplateResolver.TryRegisterPartial(handlebars, partialName, (string) context.Extensions.Optional("templatePath"))) + || !configuration.PartialTemplateResolver.TryRegisterPartial(handlebars, partialName, (string?) context.Extensions.Optional("templatePath"))) { // Template not found. return false; @@ -268,14 +268,14 @@ void IncreaseDepth() using (var encodedInner = new EncodedTextWriter(innerWriter, configuration.TextEncoder, FormatterProvider.Current, true)) { using var textWriter = encodedInner.CreateWrapper(); - configuration.RegisteredTemplates[partialName](textWriter, context); + configuration.RegisteredTemplates[partialName]!(textWriter, context); } WriteWithIndent(writer, innerWriter.ToString(), indent); } else { using var textWriter = writer.CreateWrapper(); - configuration.RegisteredTemplates[partialName](textWriter, context); + configuration.RegisteredTemplates[partialName]!(textWriter, context); } return true; } diff --git a/source/Handlebars/Compiler/Translation/Expression/PathBinder.cs b/source/Handlebars/Compiler/Translation/Expression/PathBinder.cs index 6dbc682a..22662fb9 100644 --- a/source/Handlebars/Compiler/Translation/Expression/PathBinder.cs +++ b/source/Handlebars/Compiler/Translation/Expression/PathBinder.cs @@ -21,17 +21,20 @@ protected override Expression VisitStatementExpression(StatementExpression sex) if (!(sex.Body is PathExpression pex)) return Visit(sex.Body); var configuration = CompilationContext.Configuration; - var pathInfo = PathInfoStore.Current.GetOrAdd(pex.Path); + var pathInfo = PathInfoStore.Current!.GetOrAdd(pex.Path); if (pex.Context != PathExpression.ResolutionContext.Parameter && !pathInfo.IsVariable && !pathInfo.IsThis - && (pathInfo.IsValidHelperLiteral || configuration.Compatibility.RelaxedHelperNaming)) + && (pathInfo.IsValidHelperLiteral +#pragma warning disable CS0618 // Type or member is obsolete + || configuration.Compatibility.RelaxedHelperNaming +#pragma warning restore CS0618 // Type or member is obsolete + )) { var pathInfoLight = new PathInfoLight(pathInfo); - Ref> helper; - if (!configuration.Helpers.TryGetValue(pathInfoLight, out helper)) + if (!configuration.Helpers.TryGetValue(pathInfoLight, out var helper)) { var lateBindHelperDescriptor = new LateBindHelperDescriptor(pathInfo); helper = new Ref>(lateBindHelperDescriptor); @@ -47,7 +50,9 @@ protected override Expression VisitStatementExpression(StatementExpression sex) var lateBindHelperDescriptor = new LateBindHelperDescriptor(pathInfo); helper = new Ref>(lateBindHelperDescriptor); } +#pragma warning disable CS0618 // Type or member is obsolete else if (configuration.Compatibility.RelaxedHelperNaming) +#pragma warning restore CS0618 // Type or member is obsolete { pathInfoLight = pathInfoLight.TagComparer(); if (!configuration.Helpers.ContainsKey(pathInfoLight)) @@ -75,7 +80,7 @@ protected override Expression VisitPathExpression(PathExpression pex) { var bindingContext = CompilationContext.Args.BindingContext; var configuration = CompilationContext.Configuration; - var pathInfo = PathInfoStore.Current.GetOrAdd(pex.Path); + var pathInfo = PathInfoStore.Current!.GetOrAdd(pex.Path); var resolvePath = Call(() => PathResolver.ResolvePath(bindingContext, pathInfo)); diff --git a/source/Handlebars/Compiler/Translation/Expression/SubExpressionVisitor.cs b/source/Handlebars/Compiler/Translation/Expression/SubExpressionVisitor.cs index 8630246b..48b69c46 100644 --- a/source/Handlebars/Compiler/Translation/Expression/SubExpressionVisitor.cs +++ b/source/Handlebars/Compiler/Translation/Expression/SubExpressionVisitor.cs @@ -35,7 +35,7 @@ private static Expression HandleMethodCallExpression(MethodCallExpression helper var options = Arg(helperCall.Arguments[1]); var context = Arg(helperCall.Arguments[2]); var arguments = Arg(helperCall.Arguments[3]); - var helper = Arg>(helperCall.Object); + var helper = Arg>(helperCall.Object!); return helper.Call(o => o.Invoke(options, context, arguments)); } diff --git a/source/Handlebars/Configuration/CompileTimeConfiguration.cs b/source/Handlebars/Configuration/CompileTimeConfiguration.cs index 8d7ded48..3bf9512e 100644 --- a/source/Handlebars/Configuration/CompileTimeConfiguration.cs +++ b/source/Handlebars/Configuration/CompileTimeConfiguration.cs @@ -21,6 +21,6 @@ public class CompileTimeConfiguration /// /// The compiler used to compile /// - public IExpressionCompiler ExpressionCompiler { get; set; } + public IExpressionCompiler? ExpressionCompiler { get; set; } } } \ No newline at end of file diff --git a/source/Handlebars/Configuration/HandlebarsConfiguration.cs b/source/Handlebars/Configuration/HandlebarsConfiguration.cs index f2668ed9..e0bda5f1 100644 --- a/source/Handlebars/Configuration/HandlebarsConfiguration.cs +++ b/source/Handlebars/Configuration/HandlebarsConfiguration.cs @@ -28,17 +28,17 @@ public sealed class HandlebarsConfiguration : IHandlebarsTemplateRegistrations /// public IAppendOnlyList HelperResolvers { get; } - public IExpressionNameResolver ExpressionNameResolver { get; set; } + public IExpressionNameResolver? ExpressionNameResolver { get; set; } - public ITextEncoder TextEncoder { get; set; } + public ITextEncoder? TextEncoder { get; set; } /// public IFormatProvider FormatProvider { get; set; } = CultureInfo.CurrentCulture; - public ViewEngineFileSystem FileSystem { get; set; } + public ViewEngineFileSystem? FileSystem { get; set; } [Obsolete("Register custom formatters using `Formatters` property")] - public string UnresolvedBindingFormatter + public string? UnresolvedBindingFormatter { get => _undefinedFormatter.FormatString; set => _undefinedFormatter.FormatString = value; @@ -57,7 +57,7 @@ public string UnresolvedBindingFormatter /// /// The handler called when a partial template cannot be found. /// - public IMissingPartialTemplateHandler MissingPartialTemplateHandler { get; set; } + public IMissingPartialTemplateHandler? MissingPartialTemplateHandler { get; set; } /// /// Maximum depth to recurse into partial templates when evaluating the template. Defaults to 100. diff --git a/source/Handlebars/Configuration/HandlebarsConfigurationAdapter.cs b/source/Handlebars/Configuration/HandlebarsConfigurationAdapter.cs index b160325b..e8f861bf 100644 --- a/source/Handlebars/Configuration/HandlebarsConfigurationAdapter.cs +++ b/source/Handlebars/Configuration/HandlebarsConfigurationAdapter.cs @@ -55,14 +55,14 @@ public HandlebarsConfigurationAdapter(HandlebarsConfiguration configuration) } public HandlebarsConfiguration UnderlingConfiguration { get; } - public IExpressionNameResolver ExpressionNameResolver => UnderlingConfiguration.ExpressionNameResolver; - public ITextEncoder TextEncoder => UnderlingConfiguration.TextEncoder; + public IExpressionNameResolver? ExpressionNameResolver => UnderlingConfiguration.ExpressionNameResolver; + public ITextEncoder? TextEncoder => UnderlingConfiguration.TextEncoder; public IFormatProvider FormatProvider => UnderlingConfiguration.FormatProvider; - public ViewEngineFileSystem FileSystem => UnderlingConfiguration.FileSystem; + public ViewEngineFileSystem? FileSystem => UnderlingConfiguration.FileSystem; public ObservableList FormatterProviders { get; } public bool ThrowOnUnresolvedBindingExpression => UnderlingConfiguration.ThrowOnUnresolvedBindingExpression; public IPartialTemplateResolver PartialTemplateResolver => UnderlingConfiguration.PartialTemplateResolver; - public IMissingPartialTemplateHandler MissingPartialTemplateHandler => UnderlingConfiguration.MissingPartialTemplateHandler; + public IMissingPartialTemplateHandler? MissingPartialTemplateHandler => UnderlingConfiguration.MissingPartialTemplateHandler; public short PartialRecursionDepthLimit => UnderlingConfiguration.PartialRecursionDepthLimit; public Compatibility Compatibility => UnderlingConfiguration.Compatibility; @@ -71,7 +71,7 @@ public HandlebarsConfigurationAdapter(HandlebarsConfiguration configuration) public ObservableList ObjectDescriptorProviders { get; } public IAppendOnlyList ExpressionMiddlewares { get; } public IAppendOnlyList AliasProviders { get; } - public IExpressionCompiler ExpressionCompiler { get; set; } + public IExpressionCompiler ExpressionCompiler { get; set; } = null!; public IReadOnlyList Features { get; } public IIndexed>> Helpers { get; } @@ -96,7 +96,7 @@ public HandlebarsConfigurationAdapter(HandlebarsConfiguration configuration) var target = new ObservableIndex, PathInfoLight.PathInfoLightEqualityComparer>(equalityComparer, existingHelpers); - var observer = ObserverBuilder>.Create(target) + var observer = ObserverBuilder>.Create(target) .OnEvent>( (@event, state) => { diff --git a/source/Handlebars/Configuration/ICompiledHandlebarsConfiguration.cs b/source/Handlebars/Configuration/ICompiledHandlebarsConfiguration.cs index 6434effa..89490701 100644 --- a/source/Handlebars/Configuration/ICompiledHandlebarsConfiguration.cs +++ b/source/Handlebars/Configuration/ICompiledHandlebarsConfiguration.cs @@ -15,16 +15,16 @@ namespace HandlebarsDotNet public interface IHandlebarsTemplateRegistrations { IIndexed> RegisteredTemplates { get; } - ViewEngineFileSystem FileSystem { get; } + ViewEngineFileSystem? FileSystem { get; } } public interface ICompiledHandlebarsConfiguration : IHandlebarsTemplateRegistrations { HandlebarsConfiguration UnderlingConfiguration { get; } - IExpressionNameResolver ExpressionNameResolver { get; } + IExpressionNameResolver? ExpressionNameResolver { get; } - ITextEncoder TextEncoder { get; } + ITextEncoder? TextEncoder { get; } IFormatProvider FormatProvider { get; } @@ -32,9 +32,9 @@ public interface ICompiledHandlebarsConfiguration : IHandlebarsTemplateRegistrat bool ThrowOnUnresolvedBindingExpression { get; } - IPartialTemplateResolver PartialTemplateResolver { get; } + IPartialTemplateResolver? PartialTemplateResolver { get; } - IMissingPartialTemplateHandler MissingPartialTemplateHandler { get; } + IMissingPartialTemplateHandler? MissingPartialTemplateHandler { get; } short PartialRecursionDepthLimit { get; } @@ -59,7 +59,7 @@ public interface ICompiledHandlebarsConfiguration : IHandlebarsTemplateRegistrat /// IAppendOnlyList AliasProviders { get; } - + /// IExpressionCompiler ExpressionCompiler { get; set; } diff --git a/source/Handlebars/Context.cs b/source/Handlebars/Context.cs index 491478e3..4ad4c48a 100644 --- a/source/Handlebars/Context.cs +++ b/source/Handlebars/Context.cs @@ -11,7 +11,7 @@ public readonly struct Context { private readonly DeferredValue _descriptor; - public readonly object Value; + public readonly object? Value; public Context(BindingContext context) { @@ -19,7 +19,7 @@ public Context(BindingContext context) _descriptor = context.Descriptor; } - public Context(BindingContext context, object value) + public Context(BindingContext context, object? value) { Value = value; _descriptor = context.Descriptor; @@ -27,22 +27,23 @@ public Context(BindingContext context, object value) public IEnumerable Properties => _descriptor.Value - .GetProperties(_descriptor.Value, Value) + .GetProperties(_descriptor.Value, Value!) .OfType() .Select(o => ChainSegment.Create(o)); - public object this[ChainSegment segment] => - _descriptor.Value.MemberAccessor.TryGetValue(Value, segment, out var value) + public object? this[ChainSegment segment] => + _descriptor.Value.MemberAccessor.TryGetValue(Value!, segment, out var value) ? value : null; - public T GetValue(ChainSegment segment) + public T? GetValue(ChainSegment segment) { - if (!_descriptor.Value.MemberAccessor.TryGetValue(Value, segment, out var obj)) return default; + if (!_descriptor.Value.MemberAccessor.TryGetValue(Value!, segment, out var obj)) return default; + if (obj == null) return (T?)obj; if (obj is T value) return value; var converter = TypeDescriptor.GetConverter(obj.GetType()); - return (T) converter.ConvertTo(obj, typeof(T)); + return (T?) converter.ConvertTo(obj, typeof(T)); } } } \ No newline at end of file diff --git a/source/Handlebars/Decorators/BlockDecoratorOptions.cs b/source/Handlebars/Decorators/BlockDecoratorOptions.cs index 156fa251..f5bf6b82 100644 --- a/source/Handlebars/Decorators/BlockDecoratorOptions.cs +++ b/source/Handlebars/Decorators/BlockDecoratorOptions.cs @@ -37,7 +37,7 @@ internal BlockDecoratorOptions( public PathInfo Name { get; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BindingContext CreateFrame(object value = null) => Frame.CreateFrame(value); + public BindingContext CreateFrame(object? value = null) => Frame.CreateFrame(value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public BindingContext CreateFrame(Context value) => Frame.CreateFrame(value.Value); @@ -53,14 +53,14 @@ public string Template() OriginalTemplate(encodedTextWriter, Frame); - return encodedTextWriter.ToString(); + return writer.ToString(); } /// /// BlockHelper body /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Template(in EncodedTextWriter writer, object context) + public void Template(in EncodedTextWriter writer, object? context) { if (context is BindingContext bindingContext) { diff --git a/source/Handlebars/DynamicViewModel.cs b/source/Handlebars/DynamicViewModel.cs index f09e1711..f899f9de 100644 --- a/source/Handlebars/DynamicViewModel.cs +++ b/source/Handlebars/DynamicViewModel.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq; using System.Reflection; @@ -22,7 +23,7 @@ public override IEnumerable GetDynamicMemberNames() .Select(m => m.Name); } - public override bool TryGetMember(GetMemberBinder binder, out object result) + public override bool TryGetMember(GetMemberBinder binder, out object? result) { result = null; foreach (var target in _objects) diff --git a/source/Handlebars/EqualityComparers/ChainSegment.ChainSegmentEqualityComparer.cs b/source/Handlebars/EqualityComparers/ChainSegment.ChainSegmentEqualityComparer.cs index ba9dbacf..0041486a 100644 --- a/source/Handlebars/EqualityComparers/ChainSegment.ChainSegmentEqualityComparer.cs +++ b/source/Handlebars/EqualityComparers/ChainSegment.ChainSegmentEqualityComparer.cs @@ -10,7 +10,7 @@ public sealed partial class ChainSegment { [DebuggerStepThrough] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ChainSegment x, ChainSegment y) + public bool Equals(ChainSegment? x, ChainSegment? y) { if (ReferenceEquals(x, y)) return true; if (ReferenceEquals(x, null)) return false; diff --git a/source/Handlebars/EqualityComparers/PathInfo.TrimmedPathEqualityComparer.cs b/source/Handlebars/EqualityComparers/PathInfo.TrimmedPathEqualityComparer.cs index 0c9e6b3d..fe333872 100644 --- a/source/Handlebars/EqualityComparers/PathInfo.TrimmedPathEqualityComparer.cs +++ b/source/Handlebars/EqualityComparers/PathInfo.TrimmedPathEqualityComparer.cs @@ -18,13 +18,13 @@ public TrimmedPathEqualityComparer(bool countParts = true, bool ignoreCase = tru _stringComparison = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; } - public bool Equals(PathInfo x, PathInfo y) + public bool Equals(PathInfo? x, PathInfo? y) { if (ReferenceEquals(x, y)) return true; if (ReferenceEquals(x, null)) return false; if (ReferenceEquals(y, null)) return false; - return (!_countParts || x.Segments.Length == y.Segments.Length) + return (!_countParts || x.Segments!.Length == y.Segments!.Length) && string.Equals(x.TrimmedPath, y.TrimmedPath, _stringComparison); } diff --git a/source/Handlebars/EqualityComparers/ReferenceEqualityComparer.cs b/source/Handlebars/EqualityComparers/ReferenceEqualityComparer.cs index a99957d6..391066f9 100644 --- a/source/Handlebars/EqualityComparers/ReferenceEqualityComparer.cs +++ b/source/Handlebars/EqualityComparers/ReferenceEqualityComparer.cs @@ -5,7 +5,7 @@ namespace HandlebarsDotNet.EqualityComparers public readonly struct ReferenceEqualityComparer : IEqualityComparer where T: class { - public bool Equals(T x, T y) => ReferenceEquals(x, y); + public bool Equals(T? x, T? y) => ReferenceEquals(x, y); public int GetHashCode(T obj) => obj.GetHashCode(); } diff --git a/source/Handlebars/EqualityComparers/StringEqualityComparer.cs b/source/Handlebars/EqualityComparers/StringEqualityComparer.cs index 9ecf4507..530fcbd0 100644 --- a/source/Handlebars/EqualityComparers/StringEqualityComparer.cs +++ b/source/Handlebars/EqualityComparers/StringEqualityComparer.cs @@ -9,7 +9,7 @@ namespace HandlebarsDotNet.EqualityComparers public StringEqualityComparer(StringComparison stringComparison) => _stringComparison = stringComparison; - public bool Equals(string x, string y) => string.Equals(x, y, _stringComparison); + public bool Equals(string? x, string? y) => string.Equals(x, y, _stringComparison); public int GetHashCode(string obj) => obj.GetHashCode(); } diff --git a/source/Handlebars/Extensions/EnumerableExtensions.cs b/source/Handlebars/Extensions/EnumerableExtensions.cs index efa39315..73ce2fd5 100644 --- a/source/Handlebars/Extensions/EnumerableExtensions.cs +++ b/source/Handlebars/Extensions/EnumerableExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using HandlebarsDotNet.Collections; @@ -70,7 +71,9 @@ public static IIndexed ToIndexed( Func keySelector, Func valueSelector, TComparer comparer - ) where TComparer : IEqualityComparer + ) + where TKey : notnull + where TComparer : IEqualityComparer { var dictionary = new DictionarySlim(comparer); foreach (var item in enumerable) @@ -81,7 +84,7 @@ TComparer comparer return dictionary; } - public static TValue Optional(this IReadOnlyIndexed indexed, in TKey key) + public static TValue? Optional(this IReadOnlyIndexed indexed, in TKey key) { indexed.TryGetValue(key, out var value); return value; @@ -101,7 +104,7 @@ public SequenceOfOneClass(T value) { _value = value; _enumerated = false; - Current = default; + Current = default!; } public SequenceOfOneClass GetEnumerator() => this; @@ -114,7 +117,7 @@ public bool MoveNext() { if (_enumerated) { - Current = default; + Current = default!; return false; } _enumerated = true; @@ -126,7 +129,7 @@ public bool MoveNext() public T Current { get; private set; } - object IEnumerator.Current => Current; + object? IEnumerator.Current => Current; public void Dispose() { diff --git a/source/Handlebars/Extensions/TypeExtensions.cs b/source/Handlebars/Extensions/TypeExtensions.cs index 94add9f9..351da73b 100644 --- a/source/Handlebars/Extensions/TypeExtensions.cs +++ b/source/Handlebars/Extensions/TypeExtensions.cs @@ -1,11 +1,12 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace HandlebarsDotNet { internal static class TypeExtensions { - public static bool IsAssignableToGenericType(this Type givenType, Type genericType, out Type resolvedType) + public static bool IsAssignableToGenericType(this Type givenType, Type genericType, [MaybeNullWhen(false)] out Type resolvedType) { while (true) { diff --git a/source/Handlebars/Features/MissingHelperFeature.cs b/source/Handlebars/Features/MissingHelperFeature.cs index 06a24293..b50bc21b 100644 --- a/source/Handlebars/Features/MissingHelperFeature.cs +++ b/source/Handlebars/Features/MissingHelperFeature.cs @@ -18,8 +18,8 @@ public static class MissingHelperFeatureExtension /// public static HandlebarsConfiguration RegisterMissingHelperHook( this HandlebarsConfiguration configuration, - IHelperDescriptor helperMissing = null, - IHelperDescriptor blockHelperMissing = null + IHelperDescriptor? helperMissing = null, + IHelperDescriptor? blockHelperMissing = null ) { var feature = new MissingHelperFeatureFactory(helperMissing, blockHelperMissing); @@ -43,8 +43,8 @@ public static HandlebarsConfiguration RegisterMissingHelperHook( /// public static HandlebarsConfiguration RegisterMissingHelperHook( this HandlebarsConfiguration configuration, - HandlebarsReturnWithOptionsHelper helperMissing = null, - HandlebarsBlockHelper blockHelperMissing = null + HandlebarsReturnWithOptionsHelper? helperMissing = null, + HandlebarsBlockHelper? blockHelperMissing = null ) { return configuration.RegisterMissingHelperHook( @@ -56,12 +56,12 @@ public static HandlebarsConfiguration RegisterMissingHelperHook( internal class MissingHelperFeatureFactory : IFeatureFactory { - private readonly IHelperDescriptor _returnHelper; - private readonly IHelperDescriptor _blockHelper; + private readonly IHelperDescriptor? _returnHelper; + private readonly IHelperDescriptor? _blockHelper; public MissingHelperFeatureFactory( - IHelperDescriptor returnHelper = null, - IHelperDescriptor blockHelper = null + IHelperDescriptor? returnHelper = null, + IHelperDescriptor? blockHelper = null ) { _returnHelper = returnHelper; @@ -77,12 +77,12 @@ internal class MissingHelperFeature : IFeature private const string HelperMissingKey = "helperMissing"; private const string BlockHelperMissingKey = "blockHelperMissing"; - private readonly IHelperDescriptor _helper; - private readonly IHelperDescriptor _blockHelper; + private readonly IHelperDescriptor? _helper; + private readonly IHelperDescriptor? _blockHelper; public MissingHelperFeature( - IHelperDescriptor helper, - IHelperDescriptor blockHelper + IHelperDescriptor? helper, + IHelperDescriptor? blockHelper ) { _helper = helper; @@ -91,7 +91,7 @@ IHelperDescriptor blockHelper public void OnCompiling(ICompiledHandlebarsConfiguration configuration) { - var helperMissingPathInfo = PathInfoStore.Current.GetOrAdd(HelperMissingKey); + var helperMissingPathInfo = PathInfoStore.Current!.GetOrAdd(HelperMissingKey); if(!configuration.Helpers.ContainsKey(helperMissingPathInfo)) { var helper = _helper ?? new MissingHelperDescriptor(); diff --git a/source/Handlebars/Features/WarmUpFeature.cs b/source/Handlebars/Features/WarmUpFeature.cs index c97f60da..114b0ff4 100644 --- a/source/Handlebars/Features/WarmUpFeature.cs +++ b/source/Handlebars/Features/WarmUpFeature.cs @@ -29,7 +29,7 @@ internal class WarmUpFeature : IFeature public void OnCompiling(ICompiledHandlebarsConfiguration configuration) { - var descriptorProvider = ObjectDescriptorFactory.Current; + var descriptorProvider = ObjectDescriptorFactory.Current!; foreach (var type in _types) { descriptorProvider.TryGetDescriptor(type, out _); diff --git a/source/Handlebars/FileSystemPartialTemplateResolver.cs b/source/Handlebars/FileSystemPartialTemplateResolver.cs index 8b0338e9..9159e16f 100644 --- a/source/Handlebars/FileSystemPartialTemplateResolver.cs +++ b/source/Handlebars/FileSystemPartialTemplateResolver.cs @@ -4,7 +4,7 @@ namespace HandlebarsDotNet { public class FileSystemPartialTemplateResolver : IPartialTemplateResolver { - public bool TryRegisterPartial(IHandlebars env, string partialName, string templatePath) + public bool TryRegisterPartial(IHandlebars env, string? partialName, string? templatePath) { if (env == null) { diff --git a/source/Handlebars/Handlebars.cs b/source/Handlebars/Handlebars.cs index 4de3f1de..68578e4f 100644 --- a/source/Handlebars/Handlebars.cs +++ b/source/Handlebars/Handlebars.cs @@ -19,7 +19,7 @@ public sealed class Handlebars /// /// /// - public static IHandlebars Create(HandlebarsConfiguration configuration = null) + public static IHandlebars Create(HandlebarsConfiguration? configuration = null) { configuration = configuration ?? new HandlebarsConfiguration(); return new HandlebarsEnvironment(configuration); @@ -31,7 +31,7 @@ public static IHandlebars Create(HandlebarsConfiguration configuration = null) /// /// /// - public static IHandlebars CreateSharedEnvironment(HandlebarsConfiguration configuration = null) + public static IHandlebars CreateSharedEnvironment(HandlebarsConfiguration? configuration = null) { configuration ??= new HandlebarsConfiguration(); return new HandlebarsEnvironment(new HandlebarsConfigurationAdapter(configuration)); diff --git a/source/Handlebars/Handlebars.csproj b/source/Handlebars/Handlebars.csproj index 31f8f111..c33ba767 100644 --- a/source/Handlebars/Handlebars.csproj +++ b/source/Handlebars/Handlebars.csproj @@ -20,6 +20,7 @@ true README.md true + enable diff --git a/source/Handlebars/HandlebarsEnvironment.cs b/source/Handlebars/HandlebarsEnvironment.cs index cfa9872c..7e20cce7 100644 --- a/source/Handlebars/HandlebarsEnvironment.cs +++ b/source/Handlebars/HandlebarsEnvironment.cs @@ -42,10 +42,10 @@ internal HandlebarsEnvironment(ICompiledHandlebarsConfiguration configuration) public bool IsSharedEnvironment { get; } public HandlebarsConfiguration Configuration { get; } - internal ICompiledHandlebarsConfiguration CompiledConfiguration { get; } - ICompiledHandlebarsConfiguration ICompiledHandlebars.CompiledConfiguration => CompiledConfiguration; + internal ICompiledHandlebarsConfiguration? CompiledConfiguration { get; } + ICompiledHandlebarsConfiguration? ICompiledHandlebars.CompiledConfiguration => CompiledConfiguration; - public HandlebarsTemplate CompileView(string templatePath, ViewReaderFactory readerFactoryFactory) + public HandlebarsTemplate CompileView(string templatePath, ViewReaderFactory? readerFactoryFactory) { readerFactoryFactory ??= ViewReaderFactory; return CompileViewInternal(templatePath, readerFactoryFactory); @@ -56,7 +56,7 @@ public HandlebarsTemplate CompileView(string templatePath) var view = CompileViewInternal(templatePath, ViewReaderFactory); return (vm, data) => { - var formatProvider = Configuration?.FormatProvider ?? CompiledConfiguration.FormatProvider; + var formatProvider = Configuration?.FormatProvider ?? CompiledConfiguration!.FormatProvider; using var writer = ReusableStringWriter.Get(formatProvider); view(writer, vm, data); return writer.ToString(); @@ -197,8 +197,8 @@ public HandlebarsTemplate Compile(string template) public void RegisterTemplate(string templateName, HandlebarsTemplate template) { - var registrations = Configuration ?? (IHandlebarsTemplateRegistrations) CompiledConfiguration; - registrations.RegisteredTemplates[templateName] = template; + var registrations = Configuration ?? (IHandlebarsTemplateRegistrations) CompiledConfiguration!; + registrations!.RegisteredTemplates[templateName] = template; } public void RegisterTemplate(string templateName, string template) diff --git a/source/Handlebars/HandlebarsException.cs b/source/Handlebars/HandlebarsException.cs index 3d0b8e14..1661cb47 100644 --- a/source/Handlebars/HandlebarsException.cs +++ b/source/Handlebars/HandlebarsException.cs @@ -12,7 +12,7 @@ public HandlebarsException(string message) { } - internal HandlebarsException(string message, IReaderContext context = null) + internal HandlebarsException(string message, IReaderContext? context = null) : this(message, null, context) { } @@ -22,12 +22,12 @@ public HandlebarsException(string message, Exception innerException) { } - internal HandlebarsException(string message, Exception innerException, IReaderContext context = null) + internal HandlebarsException(string message, Exception? innerException, IReaderContext? context = null) : base(FormatMessage(message, context), innerException) { } - private static string FormatMessage(string message, IReaderContext context) + private static string FormatMessage(string message, IReaderContext? context) { if (context == null) return message; diff --git a/source/Handlebars/HandlebarsExtensions.cs b/source/Handlebars/HandlebarsExtensions.cs index e6d00268..3bc22177 100644 --- a/source/Handlebars/HandlebarsExtensions.cs +++ b/source/Handlebars/HandlebarsExtensions.cs @@ -10,7 +10,7 @@ public static class HandlebarsExtensions /// /// /// - public static void WriteSafeString(this in EncodedTextWriter writer, string value) + public static void WriteSafeString(this in EncodedTextWriter writer, string? value) { writer.Write(value, false); } @@ -20,7 +20,7 @@ public static void WriteSafeString(this in EncodedTextWriter writer, string valu /// /// /// - public static void WriteSafeString(this in EncodedTextWriter writer, object value) + public static void WriteSafeString(this in EncodedTextWriter writer, object? value) { if (value is string str) { diff --git a/source/Handlebars/HandlebarsRuntimeException.cs b/source/Handlebars/HandlebarsRuntimeException.cs index f331a119..db3e05e6 100644 --- a/source/Handlebars/HandlebarsRuntimeException.cs +++ b/source/Handlebars/HandlebarsRuntimeException.cs @@ -12,7 +12,7 @@ public HandlebarsRuntimeException(string message) { } - internal HandlebarsRuntimeException(string message, IReaderContext context = null) + internal HandlebarsRuntimeException(string message, IReaderContext? context = null) : this(message, null, context) { } @@ -22,7 +22,7 @@ public HandlebarsRuntimeException(string message, Exception innerException) { } - internal HandlebarsRuntimeException(string message, Exception innerException, IReaderContext context = null) + internal HandlebarsRuntimeException(string message, Exception? innerException, IReaderContext? context = null) : base(message, innerException, context) { } diff --git a/source/Handlebars/HandlebarsUtils.cs b/source/Handlebars/HandlebarsUtils.cs index 01581869..c5a7307c 100644 --- a/source/Handlebars/HandlebarsUtils.cs +++ b/source/Handlebars/HandlebarsUtils.cs @@ -1,6 +1,7 @@ using System; using HandlebarsDotNet.Compiler; using System.Collections; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet { @@ -12,7 +13,7 @@ public static class HandlebarsUtils /// /// Determined whether the numerical 0 is considered to be truthy. Default false /// - public static bool IsTruthy(object value, bool includeZero = false) + public static bool IsTruthy([NotNullWhen(true)] object? value, bool includeZero = false) { return !IsFalsy(value, includeZero); } @@ -23,7 +24,7 @@ public static bool IsTruthy(object value, bool includeZero = false) /// The value whose falsy-ness is to be determined /// Determined whether the numerical 0 is considered to be truthy /// - public static bool IsFalsy(object value, bool includeZero) + public static bool IsFalsy([NotNullWhen(false)] object? value, bool includeZero) { switch (value) { @@ -45,12 +46,12 @@ public static bool IsFalsy(object value, bool includeZero) return false; } - public static bool IsTruthyOrNonEmpty(object value, bool includeZero = false) + public static bool IsTruthyOrNonEmpty([NotNullWhen(true)] object? value, bool includeZero = false) { return !IsFalsyOrEmpty(value, includeZero); } - public static bool IsFalsyOrEmpty(object value, bool includeZero = false) + public static bool IsFalsyOrEmpty([NotNullWhen(false)] object? value, bool includeZero = false) { if(IsFalsy(value, includeZero)) { @@ -60,7 +61,7 @@ public static bool IsFalsyOrEmpty(object value, bool includeZero = false) return value is IEnumerable enumerable && !enumerable.Any(); } - private static bool IsNumber(object value) + private static bool IsNumber([NotNullWhen(true)] object? value) { return value is sbyte || value is byte diff --git a/source/Handlebars/HandlebarsViewEngine.cs b/source/Handlebars/HandlebarsViewEngine.cs index 02d87011..be69bb6d 100644 --- a/source/Handlebars/HandlebarsViewEngine.cs +++ b/source/Handlebars/HandlebarsViewEngine.cs @@ -8,9 +8,9 @@ namespace HandlebarsDotNet public abstract class ViewEngineFileSystem { - public abstract string GetFileContent(string filename); + public abstract string? GetFileContent(string filename); - private static string GetDir(string currentFilePath) + private static string? GetDir(string currentFilePath) { if (currentFilePath == "") return null; var parts = currentFilePath.Split(new[] {'\\', '/'}); @@ -18,7 +18,7 @@ private static string GetDir(string currentFilePath) return string.Join("/", parts.Take(parts.Length - 1)); } - public string Closest(string filename, string otherFileName) + public string? Closest(string filename, string otherFileName) { var dir = GetDir(filename); while (true) diff --git a/source/Handlebars/Helpers/BlockHelpers/DelegateReturnBlockHelperDescriptor.cs b/source/Handlebars/Helpers/BlockHelpers/DelegateReturnBlockHelperDescriptor.cs index 3345fb1a..b0d6669d 100644 --- a/source/Handlebars/Helpers/BlockHelpers/DelegateReturnBlockHelperDescriptor.cs +++ b/source/Handlebars/Helpers/BlockHelpers/DelegateReturnBlockHelperDescriptor.cs @@ -14,7 +14,7 @@ public DelegateReturnBlockHelperDescriptor(string name, HandlebarsReturnBlockHel public PathInfo Name { get; } - public object Invoke(in BlockHelperOptions options, in Context context, in Arguments arguments) + public object? Invoke(in BlockHelperOptions options, in Context context, in Arguments arguments) { return _helper(options, context, arguments); } diff --git a/source/Handlebars/Helpers/BlockHelpers/LateBindBlockHelperDescriptor.cs b/source/Handlebars/Helpers/BlockHelpers/LateBindBlockHelperDescriptor.cs index 96ae9790..c32c53e5 100644 --- a/source/Handlebars/Helpers/BlockHelpers/LateBindBlockHelperDescriptor.cs +++ b/source/Handlebars/Helpers/BlockHelpers/LateBindBlockHelperDescriptor.cs @@ -36,7 +36,7 @@ public void Invoke(in EncodedTextWriter output, in BlockHelperOptions options, i } } - configuration.BlockHelpers["blockHelperMissing"].Value + configuration.BlockHelpers["blockHelperMissing"]!.Value .Invoke(output, options, context, arguments); } } diff --git a/source/Handlebars/Helpers/BlockHelpers/MissingBlockHelperDescriptor.cs b/source/Handlebars/Helpers/BlockHelpers/MissingBlockHelperDescriptor.cs index 3a5b0ce6..35c0838b 100644 --- a/source/Handlebars/Helpers/BlockHelpers/MissingBlockHelperDescriptor.cs +++ b/source/Handlebars/Helpers/BlockHelpers/MissingBlockHelperDescriptor.cs @@ -25,7 +25,7 @@ public void Invoke(in EncodedTextWriter output, in BlockHelperOptions options, i RenderSection(value, bindingContext, output, options.OriginalTemplate, options.OriginalInverse); } - private static void RenderSection(object value, + private static void RenderSection(object? value, BindingContext context, EncodedTextWriter writer, TemplateDelegate body, diff --git a/source/Handlebars/Helpers/BlockHelpers/WithBlockHelperDescriptor.cs b/source/Handlebars/Helpers/BlockHelpers/WithBlockHelperDescriptor.cs index af45a6f6..a6094dad 100644 --- a/source/Handlebars/Helpers/BlockHelpers/WithBlockHelperDescriptor.cs +++ b/source/Handlebars/Helpers/BlockHelpers/WithBlockHelperDescriptor.cs @@ -26,7 +26,7 @@ public void Invoke(in EncodedTextWriter output, in BlockHelperOptions options, i using var frame = options.CreateFrame(arguments[0]); var blockParamsValues = frame.BlockParams(options.BlockVariables); - blockParamsValues[0] = arguments[0]; + blockParamsValues[0] = arguments[0]!; options.Template(output, frame); } diff --git a/source/Handlebars/Helpers/DelegateReturnHelperDescriptor.cs b/source/Handlebars/Helpers/DelegateReturnHelperDescriptor.cs index 5a0c26ed..6d875156 100644 --- a/source/Handlebars/Helpers/DelegateReturnHelperDescriptor.cs +++ b/source/Handlebars/Helpers/DelegateReturnHelperDescriptor.cs @@ -14,7 +14,7 @@ public DelegateReturnHelperDescriptor(string name, HandlebarsReturnHelper helper public PathInfo Name { get; } - public object Invoke(in HelperOptions options, in Context context, in Arguments arguments) + public object? Invoke(in HelperOptions options, in Context context, in Arguments arguments) { return _helper(context, arguments); } diff --git a/source/Handlebars/Helpers/DelegateReturnHelperWithOptionsDescriptor.cs b/source/Handlebars/Helpers/DelegateReturnHelperWithOptionsDescriptor.cs index 66624960..b37dcfba 100644 --- a/source/Handlebars/Helpers/DelegateReturnHelperWithOptionsDescriptor.cs +++ b/source/Handlebars/Helpers/DelegateReturnHelperWithOptionsDescriptor.cs @@ -14,7 +14,7 @@ public DelegateReturnHelperWithOptionsDescriptor(string name, HandlebarsReturnWi public PathInfo Name { get; } - public object Invoke(in HelperOptions options, in Context context, in Arguments arguments) + public object? Invoke(in HelperOptions options, in Context context, in Arguments arguments) { return _helper(options, context, arguments); } diff --git a/source/Handlebars/Helpers/HelperExtensions.cs b/source/Handlebars/Helpers/HelperExtensions.cs index 3fb45f57..630805f0 100644 --- a/source/Handlebars/Helpers/HelperExtensions.cs +++ b/source/Handlebars/Helpers/HelperExtensions.cs @@ -23,7 +23,7 @@ in Arguments arguments // Return a SafeString so the captured output — which already has the correct // encoding applied by the EncodedTextWriter — is not encoded a second time // when it is passed as an argument to an outer helper. - return new SafeString(output.ToString()); + return new SafeString(writer.ToString()); } } } \ No newline at end of file diff --git a/source/Handlebars/Helpers/IHelperDescriptor.cs b/source/Handlebars/Helpers/IHelperDescriptor.cs index cabdd601..2039712c 100644 --- a/source/Handlebars/Helpers/IHelperDescriptor.cs +++ b/source/Handlebars/Helpers/IHelperDescriptor.cs @@ -10,7 +10,7 @@ public interface IHelperDescriptor public interface IHelperDescriptor : IHelperDescriptor, IDescriptor where TOptions: struct, IHelperOptions { - object Invoke(in TOptions options, in Context context, in Arguments arguments); + object? Invoke(in TOptions options, in Context context, in Arguments arguments); void Invoke(in EncodedTextWriter output, in TOptions options, in Context context, in Arguments arguments); } } \ No newline at end of file diff --git a/source/Handlebars/Helpers/IHelperResolver.cs b/source/Handlebars/Helpers/IHelperResolver.cs index b757131e..54d79994 100644 --- a/source/Handlebars/Helpers/IHelperResolver.cs +++ b/source/Handlebars/Helpers/IHelperResolver.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using HandlebarsDotNet.PathStructure; namespace HandlebarsDotNet.Helpers @@ -15,7 +16,7 @@ public interface IHelperResolver /// /// /// - bool TryResolveHelper(PathInfo name, Type targetType, out IHelperDescriptor helper); + bool TryResolveHelper(PathInfo name, Type? targetType, [MaybeNullWhen(false)] out IHelperDescriptor helper); /// /// Resolves @@ -23,6 +24,6 @@ public interface IHelperResolver /// /// /// - bool TryResolveBlockHelper(PathInfo name, out IHelperDescriptor helper); + bool TryResolveBlockHelper(PathInfo name, [MaybeNullWhen(false)] out IHelperDescriptor helper); } } \ No newline at end of file diff --git a/source/Handlebars/Helpers/LateBindHelperDescriptor.cs b/source/Handlebars/Helpers/LateBindHelperDescriptor.cs index 8230f93a..af49fa7e 100644 --- a/source/Handlebars/Helpers/LateBindHelperDescriptor.cs +++ b/source/Handlebars/Helpers/LateBindHelperDescriptor.cs @@ -9,7 +9,7 @@ public sealed class LateBindHelperDescriptor : IHelperDescriptor public PathInfo Name { get; } - public object Invoke(in HelperOptions options, in Context context, in Arguments arguments) + public object? Invoke(in HelperOptions options, in Context context, in Arguments arguments) { var bindingContext = options.Frame; @@ -21,9 +21,9 @@ public object Invoke(in HelperOptions options, in Context context, in Arguments // TODO: add cache var configuration = options.Frame.Configuration; var helperResolvers = (ObservableList) configuration.HelperResolvers; - if(helperResolvers.Count != 0) + if (helperResolvers.Count != 0) { - var targetType = arguments.Length > 0 ? arguments[0].GetType() : null; + var targetType = arguments.Length > 0 ? arguments[0]!.GetType() : null; for (var index = 0; index < helperResolvers.Count; index++) { var resolver = helperResolvers[index]; @@ -36,7 +36,7 @@ public object Invoke(in HelperOptions options, in Context context, in Arguments var value = PathResolver.ResolvePath(bindingContext, Name); if (!(value is UndefinedBindingResult)) return value; - return configuration.Helpers["helperMissing"].Value.Invoke(options, context, arguments); + return configuration.Helpers["helperMissing"]!.Value.Invoke(options, context, arguments); } public void Invoke(in EncodedTextWriter output, in HelperOptions options, in Context context, in Arguments arguments) diff --git a/source/Handlebars/Helpers/LookupReturnHelperDescriptor.cs b/source/Handlebars/Helpers/LookupReturnHelperDescriptor.cs index 7b8f80ff..e08bb076 100644 --- a/source/Handlebars/Helpers/LookupReturnHelperDescriptor.cs +++ b/source/Handlebars/Helpers/LookupReturnHelperDescriptor.cs @@ -6,14 +6,14 @@ public sealed class LookupReturnHelperDescriptor : IHelperDescriptor(TContext context, TData data = null) + public delegate string HandlebarsTemplate(TContext? context, TData? data = null) where TData: class where TContext: class; - public delegate void HandlebarsTemplate(TWriter writer, TContext context, TData data = null) + public delegate void HandlebarsTemplate(TWriter writer, TContext? context, TData? data = null) where TWriter: TextWriter where TData: class where TContext: class; @@ -38,7 +38,7 @@ public interface IHandlebars : IHelpersRegistry HandlebarsTemplate CompileView(string templatePath); - HandlebarsTemplate CompileView(string templatePath, ViewReaderFactory readerFactoryFactory); + HandlebarsTemplate CompileView(string templatePath, ViewReaderFactory? readerFactoryFactory); HandlebarsConfiguration Configuration { get; } @@ -71,7 +71,7 @@ public interface IHandlebars : IHelpersRegistry internal interface ICompiledHandlebars { - ICompiledHandlebarsConfiguration CompiledConfiguration { get; } + ICompiledHandlebarsConfiguration? CompiledConfiguration { get; } } } diff --git a/source/Handlebars/IHelperOptions.cs b/source/Handlebars/IHelperOptions.cs index 25565744..316d27cd 100644 --- a/source/Handlebars/IHelperOptions.cs +++ b/source/Handlebars/IHelperOptions.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using HandlebarsDotNet.PathStructure; using HandlebarsDotNet.ValueProviders; @@ -13,14 +14,14 @@ public interface IHelperOptions : IOptions public static class HelperOptionsExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryAccessMember(this T helperOptions, object instance, ChainSegment chainSegment, out object value) + public static bool TryAccessMember(this T helperOptions, object? instance, ChainSegment chainSegment, [MaybeNullWhen(false)] out object value) where T: IHelperOptions { return PathResolver.TryAccessMember(helperOptions.Frame, instance, chainSegment, out value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static object ResolvePath(this T helperOptions, PathInfo pathInfo) + public static object? ResolvePath(this T helperOptions, PathInfo pathInfo) where T: IHelperOptions { return PathResolver.ResolvePath(helperOptions.Frame, pathInfo); diff --git a/source/Handlebars/IO/EncodedTextWriter.cs b/source/Handlebars/IO/EncodedTextWriter.cs index a275aaaf..b152115a 100644 --- a/source/Handlebars/IO/EncodedTextWriter.cs +++ b/source/Handlebars/IO/EncodedTextWriter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; @@ -11,7 +12,7 @@ namespace HandlebarsDotNet { public readonly struct EncodedTextWriter : IDisposable { - private readonly IFormatterProvider _formatterProvider; + private readonly IFormatterProvider? _formatterProvider; private readonly TextEncoderWrapper _encoder; internal readonly TextWriter UnderlyingWriter; @@ -28,8 +29,8 @@ public bool SuppressEncoding [MethodImpl(MethodImplOptions.AggressiveInlining)] public EncodedTextWriter( TextWriter writer, - ITextEncoder encoder, - IFormatterProvider formatterProvider, + ITextEncoder? encoder, + IFormatterProvider? formatterProvider, bool suppressEncoding = false) { UnderlyingWriter = writer; @@ -45,7 +46,7 @@ public EncodedTextWriter( public TextWriter CreateWrapper() => EncodedTextWriterWrapper.From(this); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(string value, bool encode) + public void Write(string? value, bool encode) { if(encode && !SuppressEncoding) { @@ -102,19 +103,19 @@ public void Write(T value, bool encode) where T: IEnumerator } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(string value) => Write(value, true); + public void Write(string? value) => Write(value, true); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(string format, params object[] arguments) => Write(string.Format(format, arguments), true); + public void Write(string format, params object?[] arguments) => Write(string.Format(format, arguments), true); [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(char value) => Write(value.SequenceOfOne(), true); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(object value) => Write(value); + public void Write(object? value) => Write(value); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(T value) + public void Write(T? value) { switch (value) { @@ -136,7 +137,7 @@ public void Write(T value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteFormatted(T value) + private void WriteFormatted([DisallowNull] T value) { var type = typeof(T); #if NETSTANDARD1_3 @@ -145,10 +146,10 @@ private void WriteFormatted(T value) if (type.IsClass) type = value.GetType(); #endif - if (!_formatterProvider.TryCreateFormatter(type, out var formatter)) + if (_formatterProvider != null && _formatterProvider.TryCreateFormatter(type, out var formatter)) + formatter.Format(value, this); + else Throw.CannotResolveFormatter(type); - - formatter.Format(value, this); } public Encoding Encoding @@ -159,11 +160,12 @@ public Encoding Encoding public void Dispose() => _encoder.Dispose(); - public override string ToString() => UnderlyingWriter.ToString(); + public override string? ToString() => UnderlyingWriter.ToString(); private static class Throw { [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] public static void CannotResolveFormatter(Type type) => throw new HandlebarsRuntimeException($"Cannot resolve formatter for type `{type}`"); } } diff --git a/source/Handlebars/IO/EncodedTextWriterWrapper.cs b/source/Handlebars/IO/EncodedTextWriterWrapper.cs index 2fb77ca8..90485a38 100644 --- a/source/Handlebars/IO/EncodedTextWriterWrapper.cs +++ b/source/Handlebars/IO/EncodedTextWriterWrapper.cs @@ -28,7 +28,7 @@ public static TextWriter From(in EncodedTextWriter encodedTextWriter) [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(StringBuilder value, bool encode) => UnderlyingWriter.Write(value, encode); - public override void Write(string value) => UnderlyingWriter.Write(value); + public override void Write(string? value) => UnderlyingWriter.Write(value); public override void Write(char value) => UnderlyingWriter.Write(value); @@ -48,7 +48,7 @@ public static TextWriter From(in EncodedTextWriter encodedTextWriter) public override void Write(uint value) => UnderlyingWriter.Write(value); - public override void Write(object value) + public override void Write(object? value) { if (value is StringBuilder builder) { diff --git a/source/Handlebars/IO/Formatters/CollectionFormatterProvider.cs b/source/Handlebars/IO/Formatters/CollectionFormatterProvider.cs index 4ce82e3c..81918e96 100644 --- a/source/Handlebars/IO/Formatters/CollectionFormatterProvider.cs +++ b/source/Handlebars/IO/Formatters/CollectionFormatterProvider.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; namespace HandlebarsDotNet.IO { @@ -9,7 +11,7 @@ public class CollectionFormatterProvider : IFormatterProvider private static readonly Type CollectionFormatterType = typeof(CollectionFormatter<,>); private static readonly Type CollectionType = typeof(ICollection<>); - public bool TryCreateFormatter(Type type, out IFormatter formatter) + public bool TryCreateFormatter(Type type, [MaybeNullWhen(false)] out IFormatter formatter) { if (!type.GetTypeInfo().IsClass || !type.IsAssignableToGenericType(CollectionType, out var genericType)) { @@ -19,18 +21,22 @@ public bool TryCreateFormatter(Type type, out IFormatter formatter) var genericTypeArgument = genericType.GetGenericArguments()[0]; var targetType = CollectionFormatterType.MakeGenericType(genericTypeArgument, type); - formatter = (IFormatter) Activator.CreateInstance(targetType); + formatter = (IFormatter) Activator.CreateInstance(targetType)!; return true; } private class CollectionFormatter : IFormatter where TCollection: class, ICollection { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { + if (value is not TCollection collection) + { + Throw.ValueOfWrongTypeOrNull(nameof(value)); + return; + } var index = 0; - var collection = value as TCollection; - var lastIndex = collection!.Count - 1; + var lastIndex = collection.Count - 1; using var enumerator = collection.GetEnumerator(); while (enumerator.MoveNext()) { @@ -43,6 +49,13 @@ public void Format(T value, in EncodedTextWriter writer) ++index; } } + + private static class Throw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] + public static void ValueOfWrongTypeOrNull(string? paramName) => throw new ArgumentNullException(paramName); + } } } } \ No newline at end of file diff --git a/source/Handlebars/IO/Formatters/DefaultFormatterProvider.cs b/source/Handlebars/IO/Formatters/DefaultFormatterProvider.cs index 34851845..643e2e7f 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatterProvider.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatterProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using HandlebarsDotNet.Collections; using HandlebarsDotNet.EqualityComparers; using HandlebarsDotNet.IO.Formatters.DefaultFormatters; @@ -26,7 +27,7 @@ public class DefaultFormatterProvider : IFormatterProvider private static readonly DefaultObjectFormatter DefaultObjectFormatter = new DefaultObjectFormatter(); - public bool TryCreateFormatter(Type type, out IFormatter formatter) + public bool TryCreateFormatter(Type type, [MaybeNullWhen(false)] out IFormatter formatter) { if (!Formatters.TryGetValue(type, out formatter)) { diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultBoolFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultBoolFormatter.cs index d5c61c0f..c4125aee 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultBoolFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultBoolFormatter.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultBoolFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { if(!(value is bool v)) throw new ArgumentException(" supposed to be bool", nameof(value)); writer.UnderlyingWriter.Write(v); diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultCharFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultCharFormatter.cs index 76187385..8233071b 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultCharFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultCharFormatter.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultCharFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { if(!(value is char v)) throw new ArgumentException(" supposed to be char", nameof(value)); writer.UnderlyingWriter.Write(v); diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultDateTimeFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultDateTimeFormatter.cs index 3992121b..772a07d3 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultDateTimeFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultDateTimeFormatter.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultDateTimeFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { if(!(value is DateTime dateTime)) throw new ArgumentException(" supposed to be DateTime", nameof(value)); writer.Write(dateTime.ToString("O"), false); diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultDecimalFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultDecimalFormatter.cs index 4c0723aa..aae784cc 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultDecimalFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultDecimalFormatter.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultDecimalFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { if(!(value is decimal v)) throw new ArgumentException(" supposed to be decimal", nameof(value)); writer.UnderlyingWriter.Write(v); diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultDoubleFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultDoubleFormatter.cs index 83c0895e..f647dee7 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultDoubleFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultDoubleFormatter.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultDoubleFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { if(!(value is double v)) throw new ArgumentException(" supposed to be double", nameof(value)); writer.UnderlyingWriter.Write(v); diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultFloatFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultFloatFormatter.cs index 6ef700ea..8f4288cc 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultFloatFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultFloatFormatter.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultFloatFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { if(!(value is float v)) throw new ArgumentException(" supposed to be float", nameof(value)); writer.UnderlyingWriter.Write(v); diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultIntFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultIntFormatter.cs index c3e472a7..92d2cb05 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultIntFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultIntFormatter.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultIntFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { if(!(value is int v)) throw new ArgumentException(" supposed to be int", nameof(value)); writer.UnderlyingWriter.Write(v); diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultLongFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultLongFormatter.cs index 417de0c1..b8dc06f6 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultLongFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultLongFormatter.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultLongFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { if(!(value is long v)) throw new ArgumentException(" supposed to be long", nameof(value)); writer.UnderlyingWriter.Write(v); diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultObjectFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultObjectFormatter.cs index f74eb51b..3f4f1d69 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultObjectFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultObjectFormatter.cs @@ -1,10 +1,12 @@ +using System.Diagnostics.CodeAnalysis; + namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultObjectFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { - writer.Write(value.ToString()); + writer.Write(value!.ToString()); } } } \ No newline at end of file diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultShortFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultShortFormatter.cs index ec44ee5a..48e8229b 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultShortFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultShortFormatter.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultShortFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { if(!(value is short v)) throw new ArgumentException(" supposed to be short", nameof(value)); writer.UnderlyingWriter.Write(v); diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultUIntFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultUIntFormatter.cs index b050ee9a..9b6b36e9 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultUIntFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultUIntFormatter.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultUIntFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { if(!(value is uint v)) throw new ArgumentException(" supposed to be uint", nameof(value)); writer.UnderlyingWriter.Write(v); diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultULongFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultULongFormatter.cs index 4080ecf7..240ae4aa 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultULongFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultULongFormatter.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultULongFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { if(!(value is ulong v)) throw new ArgumentException(" supposed to be ulong", nameof(value)); writer.UnderlyingWriter.Write(v); diff --git a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultUShortFormatter.cs b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultUShortFormatter.cs index 599530d7..91626044 100644 --- a/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultUShortFormatter.cs +++ b/source/Handlebars/IO/Formatters/DefaultFormatters/DefaultUShortFormatter.cs @@ -1,10 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO.Formatters.DefaultFormatters { public class DefaultUShortFormatter : IFormatter { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { if(!(value is ushort v)) throw new ArgumentException(" supposed to be ushort", nameof(value)); writer.UnderlyingWriter.Write(v); diff --git a/source/Handlebars/IO/Formatters/FormatterProvider.cs b/source/Handlebars/IO/Formatters/FormatterProvider.cs index 09ab1a92..dd82e747 100644 --- a/source/Handlebars/IO/Formatters/FormatterProvider.cs +++ b/source/Handlebars/IO/Formatters/FormatterProvider.cs @@ -1,23 +1,16 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using HandlebarsDotNet.Collections; using HandlebarsDotNet.EqualityComparers; using HandlebarsDotNet.Runtime; -using LookupSlim = HandlebarsDotNet.Collections.LookupSlim>, HandlebarsDotNet.IO.IFormatter>, HandlebarsDotNet.EqualityComparers.ReferenceEqualityComparer>; +using LookupSlim = HandlebarsDotNet.Collections.LookupSlim>, HandlebarsDotNet.IO.IFormatter?>, HandlebarsDotNet.EqualityComparers.ReferenceEqualityComparer>; namespace HandlebarsDotNet.IO { public class FormatterProvider : IFormatterProvider { - private static readonly Func, DeferredValue>, IFormatter>> ValueFactory = (t, providers) => - { - return new DeferredValue>, IFormatter>( - new KeyValuePair>(t, providers), - DeferredValueFactory - ); - }; - - private static readonly Func>, IFormatter> DeferredValueFactory = deps => + private static readonly Func>, IFormatter?> DeferredValueFactory = deps => { var formatterProviders = deps.Value; for (var index = formatterProviders.Count - 1; index >= 0; index--) @@ -29,20 +22,28 @@ public class FormatterProvider : IFormatterProvider return null; }; - public static FormatterProvider Current => AmbientContext.Current?.FormatterProvider; + private static readonly Func, DeferredValue>, IFormatter?>> ValueFactory = (t, providers) => + { + return new DeferredValue>, IFormatter?>( + new KeyValuePair>(t, providers), + DeferredValueFactory + ); + }; + + public static FormatterProvider? Current => AmbientContext.Current?.FormatterProvider; private readonly LookupSlim _formatters = new LookupSlim(new ReferenceEqualityComparer()); private readonly ObservableList _formatterProviders; private readonly List _observers = new List(); - public FormatterProvider(ObservableList providers = null) + public FormatterProvider(ObservableList? providers = null) { _formatterProviders = new ObservableList(); if (providers != null) Append(providers); - var observer = ObserverBuilder>.Create(_formatters) + var observer = ObserverBuilder>.Create(_formatters) .OnEvent>((@event, state) => state.Clear()) .Build(); @@ -67,7 +68,7 @@ public FormatterProvider Append(FormatterProvider provider) return this; } - public bool TryCreateFormatter(Type type, out IFormatter formatter) + public bool TryCreateFormatter(Type type, [MaybeNullWhen(false)] out IFormatter formatter) { formatter = _formatters.GetOrAdd(type, ValueFactory, _formatterProviders).Value; return formatter != null; diff --git a/source/Handlebars/IO/Formatters/IFormatterProvider.cs b/source/Handlebars/IO/Formatters/IFormatterProvider.cs index 748a6e7a..bfa016f7 100644 --- a/source/Handlebars/IO/Formatters/IFormatterProvider.cs +++ b/source/Handlebars/IO/Formatters/IFormatterProvider.cs @@ -1,9 +1,10 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO { public interface IFormatterProvider { - bool TryCreateFormatter(Type type, out IFormatter formatter); + bool TryCreateFormatter(Type type, [MaybeNullWhen(false)] out IFormatter formatter); } } \ No newline at end of file diff --git a/source/Handlebars/IO/Formatters/ReadOnlyCollectionFormatterProvider.cs b/source/Handlebars/IO/Formatters/ReadOnlyCollectionFormatterProvider.cs index cfddae43..0018d600 100644 --- a/source/Handlebars/IO/Formatters/ReadOnlyCollectionFormatterProvider.cs +++ b/source/Handlebars/IO/Formatters/ReadOnlyCollectionFormatterProvider.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; namespace HandlebarsDotNet.IO { @@ -9,7 +11,7 @@ public class ReadOnlyCollectionFormatterProvider : IFormatterProvider private static readonly Type CollectionFormatterType = typeof(ReadOnlyCollectionFormatter<,>); private static readonly Type CollectionType = typeof(IReadOnlyCollection<>); - public bool TryCreateFormatter(Type type, out IFormatter formatter) + public bool TryCreateFormatter(Type type, [MaybeNullWhen(false)] out IFormatter formatter) { if (!type.GetTypeInfo().IsClass || !type.IsAssignableToGenericType(CollectionType, out var genericType)) { @@ -19,18 +21,22 @@ public bool TryCreateFormatter(Type type, out IFormatter formatter) var genericTypeArgument = genericType.GetGenericArguments()[0]; var targetType = CollectionFormatterType.MakeGenericType(genericTypeArgument, type); - formatter = (IFormatter) Activator.CreateInstance(targetType); + formatter = (IFormatter) Activator.CreateInstance(targetType)!; return true; } private class ReadOnlyCollectionFormatter : IFormatter where TCollection: class, IReadOnlyCollection { - public void Format(T value, in EncodedTextWriter writer) + public void Format([NotNull] T value, in EncodedTextWriter writer) { + if (value is not TCollection collection) + { + Throw.ValueOfWrongTypeOrNull(nameof(value)); + return; + } var index = 0; - var collection = value as TCollection; - var lastIndex = collection!.Count - 1; + var lastIndex = collection.Count - 1; using var enumerator = collection.GetEnumerator(); while (enumerator.MoveNext()) { @@ -43,6 +49,13 @@ public void Format(T value, in EncodedTextWriter writer) ++index; } } + + private static class Throw + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] + public static void ValueOfWrongTypeOrNull(string? paramName) => throw new ArgumentNullException(paramName); + } } } } \ No newline at end of file diff --git a/source/Handlebars/IO/Formatters/UndefinedFormatter.cs b/source/Handlebars/IO/Formatters/UndefinedFormatter.cs index 53c80e56..9d8f7234 100644 --- a/source/Handlebars/IO/Formatters/UndefinedFormatter.cs +++ b/source/Handlebars/IO/Formatters/UndefinedFormatter.cs @@ -1,14 +1,15 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.IO { public sealed class UndefinedFormatter : IFormatter, IFormatterProvider { - public UndefinedFormatter(string formatString = null) => FormatString = formatString; + public UndefinedFormatter(string? formatString = null) => FormatString = formatString; - public string FormatString { get; set; } + public string? FormatString { get; set; } - public bool TryCreateFormatter(Type type, out IFormatter formatter) + public bool TryCreateFormatter(Type type, [MaybeNullWhen(false)] out IFormatter formatter) { if (type != typeof(UndefinedBindingResult)) { @@ -22,7 +23,7 @@ public bool TryCreateFormatter(Type type, out IFormatter formatter) public void Format(T value, in EncodedTextWriter writer) { - if (string.IsNullOrEmpty(FormatString)) return; + if (FormatString is not {Length: > 0}) return; writer.Write(FormatString, (value as UndefinedBindingResult)!.Value); } diff --git a/source/Handlebars/IO/HtmlEncoder.cs b/source/Handlebars/IO/HtmlEncoder.cs index 95e3e942..7a5459bc 100644 --- a/source/Handlebars/IO/HtmlEncoder.cs +++ b/source/Handlebars/IO/HtmlEncoder.cs @@ -13,7 +13,7 @@ namespace HandlebarsDotNet public class HtmlEncoder : ITextEncoder { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Encode(StringBuilder text, TextWriter target) + public void Encode(StringBuilder? text, TextWriter target) { if(text == null || text.Length == 0) return; @@ -21,15 +21,15 @@ public void Encode(StringBuilder text, TextWriter target) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Encode(string text, TextWriter target) + public void Encode(string? text, TextWriter target) { - if(string.IsNullOrEmpty(text)) return; + if (text is not {Length: > 0}) return; EncodeImpl(new StringEnumerator(text), target); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Encode(T text, TextWriter target) where T : IEnumerator + public void Encode(T? text, TextWriter target) where T : IEnumerator { if (text is null) return; diff --git a/source/Handlebars/IO/HtmlEncoderLegacy.cs b/source/Handlebars/IO/HtmlEncoderLegacy.cs index 2d6ba88a..354d446d 100644 --- a/source/Handlebars/IO/HtmlEncoderLegacy.cs +++ b/source/Handlebars/IO/HtmlEncoderLegacy.cs @@ -17,7 +17,7 @@ namespace HandlebarsDotNet public class HtmlEncoderLegacy : ITextEncoder { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Encode(StringBuilder text, TextWriter target) + public void Encode(StringBuilder? text, TextWriter target) { if (text == null || text.Length == 0) return; @@ -25,15 +25,15 @@ public void Encode(StringBuilder text, TextWriter target) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Encode(string text, TextWriter target) + public void Encode(string? text, TextWriter target) { - if (string.IsNullOrEmpty(text)) return; + if (text is not {Length: > 0}) return; EncodeImpl(new StringEnumerator(text), target); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Encode(T text, TextWriter target) where T : IEnumerator + public void Encode(T? text, TextWriter target) where T : IEnumerator { if (text is null) return; diff --git a/source/Handlebars/IO/ITextEncoder.cs b/source/Handlebars/IO/ITextEncoder.cs index 3c9cb4c2..d2c63ce7 100644 --- a/source/Handlebars/IO/ITextEncoder.cs +++ b/source/Handlebars/IO/ITextEncoder.cs @@ -9,10 +9,10 @@ namespace HandlebarsDotNet /// public interface ITextEncoder { - void Encode(StringBuilder text, TextWriter target); + void Encode(StringBuilder? text, TextWriter target); - void Encode(string text, TextWriter target); + void Encode(string? text, TextWriter target); - void Encode(T text, TextWriter target) where T: IEnumerator; + void Encode(T? text, TextWriter target) where T: IEnumerator; } } \ No newline at end of file diff --git a/source/Handlebars/IO/PolledStringWriter.cs b/source/Handlebars/IO/PolledStringWriter.cs index 5f3c2ff1..0bc67532 100644 --- a/source/Handlebars/IO/PolledStringWriter.cs +++ b/source/Handlebars/IO/PolledStringWriter.cs @@ -9,9 +9,9 @@ public class ReusableStringWriter : StringWriter { private static readonly InternalObjectPool Pool = new InternalObjectPool(new Policy(16)); - private IFormatProvider _formatProvider; + private IFormatProvider _formatProvider = null!; - public static ReusableStringWriter Get(IFormatProvider formatProvider = null) + public static ReusableStringWriter Get(IFormatProvider? formatProvider = null) { var writer = Pool.Get(); writer._formatProvider = formatProvider ?? CultureInfo.CurrentCulture; @@ -26,7 +26,7 @@ private ReusableStringWriter() protected override void Dispose(bool disposing) { - _formatProvider = null; + _formatProvider = null!; Pool.Return(this); } diff --git a/source/Handlebars/IO/TextEncoderWrapper.cs b/source/Handlebars/IO/TextEncoderWrapper.cs index 823fa3a8..f1e06e60 100644 --- a/source/Handlebars/IO/TextEncoderWrapper.cs +++ b/source/Handlebars/IO/TextEncoderWrapper.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; using HandlebarsDotNet.Pools; @@ -11,12 +12,12 @@ internal sealed class TextEncoderWrapper : ITextEncoder, IDisposable private static readonly InternalObjectPool Pool = new InternalObjectPool(new Policy()); - private ITextEncoder _underlyingEncoder; + private ITextEncoder? _underlyingEncoder; private bool _enabled; public static TextEncoderWrapper Null { get; } = new TextEncoderWrapper(); - public static TextEncoderWrapper Create(ITextEncoder encoder) + public static TextEncoderWrapper Create(ITextEncoder? encoder) { var wrapper = Pool.Get(); wrapper._underlyingEncoder = encoder; @@ -25,6 +26,7 @@ public static TextEncoderWrapper Create(ITextEncoder encoder) return wrapper; } + [MemberNotNullWhen(true, nameof(_underlyingEncoder))] public bool Enabled { get => _enabled && _underlyingEncoder != null; @@ -35,21 +37,21 @@ private TextEncoderWrapper() { } - public void Encode(StringBuilder text, TextWriter target) + public void Encode(StringBuilder? text, TextWriter target) { if (!Enabled) return; _underlyingEncoder.Encode(text, target); } - public void Encode(string text, TextWriter target) + public void Encode(string? text, TextWriter target) { if (!Enabled) return; _underlyingEncoder.Encode(text, target); } - public void Encode(T text, TextWriter target) where T: IEnumerator + public void Encode(T? text, TextWriter target) where T: IEnumerator { if (!Enabled) return; diff --git a/source/Handlebars/IPartialTemplateResolver.cs b/source/Handlebars/IPartialTemplateResolver.cs index 4481d6f7..8fbe1bb0 100644 --- a/source/Handlebars/IPartialTemplateResolver.cs +++ b/source/Handlebars/IPartialTemplateResolver.cs @@ -12,6 +12,6 @@ public interface IPartialTemplateResolver /// The name of the partial to load. /// /// True if the partial was found and loaded successfully. Otherwise false. - bool TryRegisterPartial(IHandlebars env, string partialName, string templatePath); + bool TryRegisterPartial(IHandlebars env, string? partialName, string? templatePath); } } diff --git a/source/Handlebars/Iterators/ArrayIterator.cs b/source/Handlebars/Iterators/ArrayIterator.cs index 15f5355e..cd2f5724 100644 --- a/source/Handlebars/Iterators/ArrayIterator.cs +++ b/source/Handlebars/Iterators/ArrayIterator.cs @@ -33,7 +33,7 @@ TemplateDelegate ifEmpty var lastIndex = count - 1; for (; index < count; index++) { - var value = (object) target[index]; + var value = (object?) target[index]; var objectIndex = BoxedValues.Int(index); if (index == 1) iterator.First = BoxedValues.False; diff --git a/source/Handlebars/Iterators/CollectionIterator'2.cs b/source/Handlebars/Iterators/CollectionIterator'2.cs index 4e39d0eb..d1bf2480 100644 --- a/source/Handlebars/Iterators/CollectionIterator'2.cs +++ b/source/Handlebars/Iterators/CollectionIterator'2.cs @@ -7,7 +7,7 @@ namespace HandlebarsDotNet.Iterators { public sealed class CollectionIterator : IIterator - where T: ICollection + where T : ICollection { public void Iterate( in EncodedTextWriter writer, @@ -36,7 +36,7 @@ TemplateDelegate ifEmpty using var enumerator = target.GetEnumerator(); while (enumerator.MoveNext()) { - var value = (object) enumerator.Current; + var value = (object?) enumerator.Current; var objectIndex = BoxedValues.Int(index); if (index == 1) iterator.First = BoxedValues.False; diff --git a/source/Handlebars/Iterators/CollectionIterator.cs b/source/Handlebars/Iterators/CollectionIterator.cs index f34ad1ff..52df7267 100644 --- a/source/Handlebars/Iterators/CollectionIterator.cs +++ b/source/Handlebars/Iterators/CollectionIterator.cs @@ -31,13 +31,10 @@ TemplateDelegate ifEmpty iterator.First = BoxedValues.True; iterator.Last = BoxedValues.False; - var enumerator = target.GetEnumerator(); - var index = 0; var lastIndex = count - 1; - while (enumerator.MoveNext()) + foreach (var value in target) { - var value = enumerator.Current; var objectIndex = BoxedValues.Int(index); if (index == 1) iterator.First = BoxedValues.False; diff --git a/source/Handlebars/Iterators/DictionaryIterator'2.cs b/source/Handlebars/Iterators/DictionaryIterator'2.cs index 01a4fffd..7eec51e5 100644 --- a/source/Handlebars/Iterators/DictionaryIterator'2.cs +++ b/source/Handlebars/Iterators/DictionaryIterator'2.cs @@ -8,6 +8,7 @@ namespace HandlebarsDotNet.Iterators { public sealed class DictionaryIterator : IIterator where TDictionary : IDictionary + where TKey : notnull { public void Iterate( in EncodedTextWriter writer, @@ -36,7 +37,7 @@ TemplateDelegate ifEmpty while (enumerator.MoveNext()) { var key = (object) enumerator.Current.Key; - var value = (object) enumerator.Current.Value; + var value = (object?) enumerator.Current.Value; iterator.Key = key; diff --git a/source/Handlebars/Iterators/EnumerableIterator'2.cs b/source/Handlebars/Iterators/EnumerableIterator'2.cs index 699414c8..a7fb61e0 100644 --- a/source/Handlebars/Iterators/EnumerableIterator'2.cs +++ b/source/Handlebars/Iterators/EnumerableIterator'2.cs @@ -38,7 +38,7 @@ TemplateDelegate ifEmpty { var current = enumerator.Current; - var value = (object) current.Value; + var value = (object?) current.Value; var indexObject = BoxedValues.Int(index); if (index == 1) iterator.First = BoxedValues.False; diff --git a/source/Handlebars/Iterators/ListIterator'2.cs b/source/Handlebars/Iterators/ListIterator'2.cs index d6cfca59..638c0d8d 100644 --- a/source/Handlebars/Iterators/ListIterator'2.cs +++ b/source/Handlebars/Iterators/ListIterator'2.cs @@ -35,7 +35,7 @@ TemplateDelegate ifEmpty var lastIndex = count - 1; for (; index < count; index++) { - var value = (object) target[index]; + var value = (object?) target[index]; var objectIndex = BoxedValues.Int(index); if (index == 1) iterator.First = BoxedValues.False; diff --git a/source/Handlebars/Iterators/ReadOnlyCollectionIterator'2.cs b/source/Handlebars/Iterators/ReadOnlyCollectionIterator'2.cs index 2a793078..dd9583d0 100644 --- a/source/Handlebars/Iterators/ReadOnlyCollectionIterator'2.cs +++ b/source/Handlebars/Iterators/ReadOnlyCollectionIterator'2.cs @@ -36,7 +36,7 @@ TemplateDelegate ifEmpty using var enumerator = target.GetEnumerator(); while (enumerator.MoveNext()) { - var value = (object) enumerator.Current; + var value = (object?) enumerator.Current; var objectIndex = BoxedValues.Int(index); if (index == 1) iterator.First = BoxedValues.False; diff --git a/source/Handlebars/Iterators/ReadOnlyDictionaryIterator'2.cs b/source/Handlebars/Iterators/ReadOnlyDictionaryIterator'2.cs index 2847a126..7032a0d6 100644 --- a/source/Handlebars/Iterators/ReadOnlyDictionaryIterator'2.cs +++ b/source/Handlebars/Iterators/ReadOnlyDictionaryIterator'2.cs @@ -8,6 +8,7 @@ namespace HandlebarsDotNet.Iterators { public sealed class ReadOnlyDictionaryIterator : IIterator where TDictionary : class, IReadOnlyDictionary + where TKey : notnull { public void Iterate( in EncodedTextWriter writer, @@ -36,7 +37,7 @@ TemplateDelegate ifEmpty while (enumerator.MoveNext()) { var key = (object) enumerator.Current.Key; - var value = (object) enumerator.Current.Value; + var value = (object?) enumerator.Current.Value; iterator.Key = key; diff --git a/source/Handlebars/Iterators/ReadOnlyListIterator'2.cs b/source/Handlebars/Iterators/ReadOnlyListIterator'2.cs index 85057623..207db6e5 100644 --- a/source/Handlebars/Iterators/ReadOnlyListIterator'2.cs +++ b/source/Handlebars/Iterators/ReadOnlyListIterator'2.cs @@ -35,7 +35,7 @@ TemplateDelegate ifEmpty var lastIndex = count - 1; for (; index < count; index++) { - var value = (object) target[index]; + var value = (object?) target[index]; var objectIndex = BoxedValues.Int(index); if (index == 1) iterator.First = BoxedValues.False; diff --git a/source/Handlebars/LayoutViewModel.cs b/source/Handlebars/LayoutViewModel.cs index 4b77d940..4b118ead 100644 --- a/source/Handlebars/LayoutViewModel.cs +++ b/source/Handlebars/LayoutViewModel.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Diagnostics.CodeAnalysis; using System.Linq; using HandlebarsDotNet.Compiler; using HandlebarsDotNet.Iterators; @@ -14,10 +15,10 @@ internal class LayoutViewModel private static readonly ChainSegment BodyChainSegment = ChainSegment.Create("body"); private readonly string _body; - private readonly object _value; + private readonly object? _value; private readonly ObjectDescriptor _valueDescriptor; - public LayoutViewModel(string body, object value) + public LayoutViewModel(string body, object? value) { _body = body; _value = value; @@ -39,7 +40,7 @@ public DescriptorProvider() (_, o) => { var vm = (LayoutViewModel) o; - IEnumerable valueProperties = vm._valueDescriptor.GetProperties(vm._valueDescriptor, vm._value); + var valueProperties = vm._valueDescriptor.GetProperties(vm._valueDescriptor, vm._value!); return BodyProperties .Concat(valueProperties.Cast()); @@ -48,7 +49,7 @@ public DescriptorProvider() ); } - public bool TryGetDescriptor(Type type, out ObjectDescriptor value) + public bool TryGetDescriptor(Type type, [MaybeNullWhen(false)] out ObjectDescriptor value) { if (type != Type) { @@ -63,7 +64,7 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value) private class MemberAccessor: IMemberAccessor { - public bool TryGetValue(object instance, ChainSegment memberName, out object value) + public bool TryGetValue(object instance, ChainSegment memberName, out object? value) { var vm = (LayoutViewModel) instance; @@ -75,8 +76,8 @@ public bool TryGetValue(object instance, ChainSegment memberName, out object val var memberAccessor = vm._valueDescriptor.MemberAccessor; - if (memberAccessor != null) - return memberAccessor.TryGetValue(vm._value, memberName, out value); + if (memberAccessor != null!) + return memberAccessor.TryGetValue(vm._value!, memberName, out value); value = default; return false; @@ -89,7 +90,7 @@ public void Iterate(in EncodedTextWriter writer, BindingContext context, ChainSe { var vm = (LayoutViewModel) input; var iterator = vm._valueDescriptor.Iterator; - iterator?.Iterate(writer, context, blockParamsVariables, vm._value, template, ifEmpty); + iterator?.Iterate(writer, context, blockParamsVariables, vm._value!, template, ifEmpty); } } } diff --git a/source/Handlebars/MemberAccessors/DictionaryAccessors/GenericDictionaryAccessor.cs b/source/Handlebars/MemberAccessors/DictionaryAccessors/GenericDictionaryAccessor.cs index b98447d0..9bf551f3 100644 --- a/source/Handlebars/MemberAccessors/DictionaryAccessors/GenericDictionaryAccessor.cs +++ b/source/Handlebars/MemberAccessors/DictionaryAccessors/GenericDictionaryAccessor.cs @@ -9,9 +9,9 @@ public sealed class GenericDictionaryAccessor : IMemberAccessor { private static readonly TypeConverter TypeConverter = TypeDescriptor.GetConverter(typeof(TK)); - public bool TryGetValue(object instance, ChainSegment memberName, out object value) + public bool TryGetValue(object instance, ChainSegment memberName, out object? value) { - var key = (TK) TypeConverter.ConvertFromString(memberName.TrimmedValue); + var key = (TK?) TypeConverter.ConvertFromString(memberName.TrimmedValue); var dictionary = (T) instance; if (key is not null && dictionary.TryGetValue(key, out var v)) { diff --git a/source/Handlebars/MemberAccessors/DictionaryAccessors/ReadOnlyGenericDictionaryAccessor.cs b/source/Handlebars/MemberAccessors/DictionaryAccessors/ReadOnlyGenericDictionaryAccessor.cs index 63ffba09..dd84ef7b 100644 --- a/source/Handlebars/MemberAccessors/DictionaryAccessors/ReadOnlyGenericDictionaryAccessor.cs +++ b/source/Handlebars/MemberAccessors/DictionaryAccessors/ReadOnlyGenericDictionaryAccessor.cs @@ -9,9 +9,9 @@ public sealed class ReadOnlyGenericDictionaryAccessor : IMemberAccess { private static readonly TypeConverter TypeConverter = TypeDescriptor.GetConverter(typeof(TK)); - public bool TryGetValue(object instance, ChainSegment memberName, out object value) + public bool TryGetValue(object instance, ChainSegment memberName, out object? value) { - var key = (TK) TypeConverter.ConvertFromString(memberName.TrimmedValue); + var key = (TK?) TypeConverter.ConvertFromString(memberName.TrimmedValue); var dictionary = (T) instance; if (key is not null && dictionary.TryGetValue(key, out var v)) { diff --git a/source/Handlebars/MemberAccessors/DictionaryAccessors/ReadOnlyStringDictionaryAccessor.cs b/source/Handlebars/MemberAccessors/DictionaryAccessors/ReadOnlyStringDictionaryAccessor.cs index 704f3c99..cff5fb5d 100644 --- a/source/Handlebars/MemberAccessors/DictionaryAccessors/ReadOnlyStringDictionaryAccessor.cs +++ b/source/Handlebars/MemberAccessors/DictionaryAccessors/ReadOnlyStringDictionaryAccessor.cs @@ -6,7 +6,7 @@ namespace HandlebarsDotNet.MemberAccessors.DictionaryAccessors public sealed class ReadOnlyStringDictionaryAccessor : IMemberAccessor where T: IReadOnlyDictionary { - public bool TryGetValue(object instance, ChainSegment memberName, out object value) + public bool TryGetValue(object instance, ChainSegment memberName, out object? value) { var dictionary = (T) instance; if (dictionary.TryGetValue(memberName.TrimmedValue, out var v)) diff --git a/source/Handlebars/MemberAccessors/DictionaryAccessors/StringDictionaryAccessor.cs b/source/Handlebars/MemberAccessors/DictionaryAccessors/StringDictionaryAccessor.cs index 6ea3d2ce..d15bc0e0 100644 --- a/source/Handlebars/MemberAccessors/DictionaryAccessors/StringDictionaryAccessor.cs +++ b/source/Handlebars/MemberAccessors/DictionaryAccessors/StringDictionaryAccessor.cs @@ -6,7 +6,7 @@ namespace HandlebarsDotNet.MemberAccessors.DictionaryAccessors public sealed class StringDictionaryAccessor : IMemberAccessor where T: IDictionary { - public bool TryGetValue(object instance, ChainSegment memberName, out object value) + public bool TryGetValue(object instance, ChainSegment memberName, out object? value) { var dictionary = (T) instance; if (dictionary.TryGetValue(memberName.TrimmedValue, out var v)) diff --git a/source/Handlebars/MemberAccessors/DictionaryMemberAccessor.cs b/source/Handlebars/MemberAccessors/DictionaryMemberAccessor.cs index a5388122..fb3f3ec3 100644 --- a/source/Handlebars/MemberAccessors/DictionaryMemberAccessor.cs +++ b/source/Handlebars/MemberAccessors/DictionaryMemberAccessor.cs @@ -5,7 +5,7 @@ namespace HandlebarsDotNet.MemberAccessors { public sealed class DictionaryMemberAccessor : IMemberAccessor { - public bool TryGetValue(object instance, ChainSegment memberName, out object value) + public bool TryGetValue(object instance, ChainSegment memberName, out object? value) { value = null; // Check if the instance is IDictionary (ie, System.Collections.Hashtable) diff --git a/source/Handlebars/MemberAccessors/DynamicMemberAccessor.cs b/source/Handlebars/MemberAccessors/DynamicMemberAccessor.cs index be256e41..79af1ebd 100644 --- a/source/Handlebars/MemberAccessors/DynamicMemberAccessor.cs +++ b/source/Handlebars/MemberAccessors/DynamicMemberAccessor.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using HandlebarsDotNet.PathStructure; @@ -6,7 +7,7 @@ namespace HandlebarsDotNet.MemberAccessors { public sealed class DynamicMemberAccessor : IMemberAccessor { - public bool TryGetValue(object instance, ChainSegment memberName, out object value) + public bool TryGetValue(object instance, ChainSegment memberName, [MaybeNullWhen(false)] out object value) { value = null; //crude handling for dynamic objects that don't have metadata @@ -15,7 +16,7 @@ public bool TryGetValue(object instance, ChainSegment memberName, out object val try { value = GetProperty(metaObjectProvider, memberName.TrimmedValue); - return value != null; + return value != null!; } catch { diff --git a/source/Handlebars/MemberAccessors/EnumerableAccessors/EnumerableMemberAccessor'2.cs b/source/Handlebars/MemberAccessors/EnumerableAccessors/EnumerableMemberAccessor'2.cs index ea5b7365..a40129b7 100644 --- a/source/Handlebars/MemberAccessors/EnumerableAccessors/EnumerableMemberAccessor'2.cs +++ b/source/Handlebars/MemberAccessors/EnumerableAccessors/EnumerableMemberAccessor'2.cs @@ -5,7 +5,7 @@ namespace HandlebarsDotNet.MemberAccessors.EnumerableAccessors public sealed class EnumerableMemberAccessor : EnumerableMemberAccessor where T: class, IEnumerable { - protected override bool TryGetValueInternal(object instance, int index, out object value) + protected override bool TryGetValueInternal(object instance, int index, out object? value) { var list = (T) instance; using var e = list.GetEnumerator(); diff --git a/source/Handlebars/MemberAccessors/EnumerableAccessors/EnumerableMemberAccessor.cs b/source/Handlebars/MemberAccessors/EnumerableAccessors/EnumerableMemberAccessor.cs index 63a6278d..32c10c72 100644 --- a/source/Handlebars/MemberAccessors/EnumerableAccessors/EnumerableMemberAccessor.cs +++ b/source/Handlebars/MemberAccessors/EnumerableAccessors/EnumerableMemberAccessor.cs @@ -14,7 +14,7 @@ public static EnumerableMemberAccessor Create(Type type) var typeArgument = genericType.GenericTypeArguments[0]; var accessorType = typeof(ListMemberAccessor<,>).MakeGenericType(genericType, typeArgument); - return (EnumerableMemberAccessor) Activator.CreateInstance(accessorType); + return (EnumerableMemberAccessor) Activator.CreateInstance(accessorType)!; } if (type.IsAssignableToGenericType(typeof(IReadOnlyList<>), out genericType)) @@ -22,7 +22,7 @@ public static EnumerableMemberAccessor Create(Type type) var typeArgument = genericType.GenericTypeArguments[0]; var accessorType = typeof(ReadOnlyListMemberAccessor<,>).MakeGenericType(genericType, typeArgument); - return (EnumerableMemberAccessor) Activator.CreateInstance(accessorType); + return (EnumerableMemberAccessor) Activator.CreateInstance(accessorType)!; } if (type.IsAssignableToGenericType(typeof(IEnumerable<>), out genericType)) @@ -30,7 +30,7 @@ public static EnumerableMemberAccessor Create(Type type) var typeArgument = genericType.GenericTypeArguments[0]; var accessorType = typeof(EnumerableMemberAccessor<,>).MakeGenericType(genericType, typeArgument); - return (EnumerableMemberAccessor) Activator.CreateInstance(accessorType); + return (EnumerableMemberAccessor) Activator.CreateInstance(accessorType)!; } return new EnumerableMemberAccessor(); @@ -38,7 +38,7 @@ public static EnumerableMemberAccessor Create(Type type) protected EnumerableMemberAccessor() { } - public virtual bool TryGetValue(object instance, ChainSegment memberName, out object value) + public virtual bool TryGetValue(object instance, ChainSegment memberName, out object? value) { if (int.TryParse(memberName.LowerInvariant, out var index) && index >= 0) return TryGetValueInternal(instance, index, out value); @@ -47,7 +47,7 @@ public virtual bool TryGetValue(object instance, ChainSegment memberName, out ob return false; } - protected virtual bool TryGetValueInternal(object instance, int index, out object value) + protected virtual bool TryGetValueInternal(object instance, int index, out object? value) { switch (instance) { diff --git a/source/Handlebars/MemberAccessors/EnumerableAccessors/ListMemberAccessor.cs b/source/Handlebars/MemberAccessors/EnumerableAccessors/ListMemberAccessor.cs index 536c894a..f458f20b 100644 --- a/source/Handlebars/MemberAccessors/EnumerableAccessors/ListMemberAccessor.cs +++ b/source/Handlebars/MemberAccessors/EnumerableAccessors/ListMemberAccessor.cs @@ -5,7 +5,7 @@ namespace HandlebarsDotNet.MemberAccessors.EnumerableAccessors public sealed class ListMemberAccessor : EnumerableMemberAccessor where T: IList { - protected override bool TryGetValueInternal(object instance, int index, out object value) + protected override bool TryGetValueInternal(object instance, int index, out object? value) { var list = (T) instance; if (list.Count <= index) diff --git a/source/Handlebars/MemberAccessors/EnumerableAccessors/ReadOnlyListMemberAccessor.cs b/source/Handlebars/MemberAccessors/EnumerableAccessors/ReadOnlyListMemberAccessor.cs index 627e565c..29c6d647 100644 --- a/source/Handlebars/MemberAccessors/EnumerableAccessors/ReadOnlyListMemberAccessor.cs +++ b/source/Handlebars/MemberAccessors/EnumerableAccessors/ReadOnlyListMemberAccessor.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.MemberAccessors.EnumerableAccessors { public sealed class ReadOnlyListMemberAccessor : EnumerableMemberAccessor where T: IReadOnlyList { - protected override bool TryGetValueInternal(object instance, int index, out object value) + protected override bool TryGetValueInternal(object instance, int index, out object? value) { var list = (T) instance; if (list.Count <= index) diff --git a/source/Handlebars/MemberAccessors/IMemberAccessor.cs b/source/Handlebars/MemberAccessors/IMemberAccessor.cs index a5635310..cfa711de 100644 --- a/source/Handlebars/MemberAccessors/IMemberAccessor.cs +++ b/source/Handlebars/MemberAccessors/IMemberAccessor.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using HandlebarsDotNet.PathStructure; namespace HandlebarsDotNet.MemberAccessors @@ -14,6 +15,6 @@ public interface IMemberAccessor /// /// /// - bool TryGetValue(object instance, ChainSegment memberName, out object value); + bool TryGetValue(object instance, ChainSegment memberName, out object? value); } } \ No newline at end of file diff --git a/source/Handlebars/MemberAccessors/MergedMemberAccessor.cs b/source/Handlebars/MemberAccessors/MergedMemberAccessor.cs index de499745..ed27f9ad 100644 --- a/source/Handlebars/MemberAccessors/MergedMemberAccessor.cs +++ b/source/Handlebars/MemberAccessors/MergedMemberAccessor.cs @@ -11,7 +11,7 @@ public MergedMemberAccessor(params IMemberAccessor[] accessors) _accessors = accessors; } - public bool TryGetValue(object instance, ChainSegment memberName, out object value) + public bool TryGetValue(object instance, ChainSegment memberName, out object? value) { for (var index = 0; index < _accessors.Length; index++) { diff --git a/source/Handlebars/MemberAccessors/ReflectionMemberAccessor.cs b/source/Handlebars/MemberAccessors/ReflectionMemberAccessor.cs index c7bb12e6..1542a6a4 100644 --- a/source/Handlebars/MemberAccessors/ReflectionMemberAccessor.cs +++ b/source/Handlebars/MemberAccessors/ReflectionMemberAccessor.cs @@ -24,7 +24,7 @@ public ReflectionMemberAccessor(IReadOnlyList aliasProvide _aliasProviders = aliasProviders; } - public bool TryGetValue(object instance, ChainSegment memberName, out object value) + public bool TryGetValue(object instance, ChainSegment memberName, out object? value) { var instanceType = instance.GetType(); if (TryGetValueImpl(instance, instanceType, memberName, out value)) return true; @@ -39,7 +39,7 @@ public bool TryGetValue(object instance, ChainSegment memberName, out object val return false; } - private bool TryGetValueImpl(object instance, Type instanceType, ChainSegment memberName, out object value) + private bool TryGetValueImpl(object instance, Type instanceType, ChainSegment memberName, out object? value) { if (!_descriptors.TryGetValue(instanceType, out var deferredValue)) { @@ -55,16 +55,17 @@ private sealed class RawObjectTypeDescriptor { private delegate TValue ValueTypeGetterDelegate(ref T instance); - private static readonly MethodInfo CreateGetDelegateMethodInfo = typeof(RawObjectTypeDescriptor) - .GetMethod(nameof(CreateGetDelegate), BindingFlags.Static | BindingFlags.NonPublic); // NOSONAR S3011 — accessing own private method by name for generic delegate construction; no external access bypassed + private static readonly MethodInfo CreateGetDelegateMethodInfo = + new Func>(CreateGetDelegate) + .GetMethodInfo().GetGenericMethodDefinition(); // NOSONAR S3011 — accessing own private method by name for generic delegate construction; no external access bypassed - private static readonly Func, Func> ValueGetterFactory = o => GetValueGetter(o.Key, o.Value); + private static readonly Func, Func?> ValueGetterFactory = o => GetValueGetter(o.Key, o.Value); - private static readonly Func, Func>> - ValueFactory = (key, state) => new DeferredValue, Func>(new KeyValuePair(key, state), ValueGetterFactory); + private static readonly Func, Func?>> + ValueFactory = (key, state) => new DeferredValue, Func?>(new KeyValuePair(key, state), ValueGetterFactory); - private readonly LookupSlim, Func>, ChainSegment.ChainSegmentEqualityComparer> - _accessors = new LookupSlim, Func>, ChainSegment.ChainSegmentEqualityComparer>(new ChainSegment.ChainSegmentEqualityComparer()); + private readonly LookupSlim, Func?>, ChainSegment.ChainSegmentEqualityComparer> + _accessors = new LookupSlim, Func?>, ChainSegment.ChainSegmentEqualityComparer>(new ChainSegment.ChainSegmentEqualityComparer()); private Type Type { get; } @@ -73,14 +74,14 @@ public RawObjectTypeDescriptor(Type type) Type = type; } - public Func GetOrCreateAccessor(ChainSegment name) + public Func? GetOrCreateAccessor(ChainSegment name) { return _accessors.TryGetValue(name, out var deferredValue) ? deferredValue.Value : _accessors.GetOrAdd(name, ValueFactory, Type).Value; } - private static Func GetValueGetter(ChainSegment name, Type type) + private static Func? GetValueGetter(ChainSegment name, Type type) { var property = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(o => o.GetIndexParameters().Length == 0 && @@ -91,7 +92,7 @@ private static Func GetValueGetter(ChainSegment name, Type type) { return (Func) CreateGetDelegateMethodInfo .MakeGenericMethod(type, property.PropertyType) - .Invoke(null, new object[] {property}); + .Invoke(null, new object[] {property})!; } var field = type.GetFields(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(o => string.Equals(o.Name, name.LowerInvariant, StringComparison.OrdinalIgnoreCase)); @@ -104,24 +105,24 @@ private static Func GetValueGetter(ChainSegment name, Type type) return null; } - private static Func CreateGetDelegate(PropertyInfo property) + private static Func CreateGetDelegate(PropertyInfo property) { - var typeInfo = property.DeclaringType.GetTypeInfo(); + var typeInfo = property.DeclaringType!.GetTypeInfo(); if (typeInfo.IsValueType) { // Value types must be passed by reference - var @delegate = (ValueTypeGetterDelegate)property.GetMethod.CreateDelegate(typeof(ValueTypeGetterDelegate)); + var @delegate = (ValueTypeGetterDelegate)property.GetMethod!.CreateDelegate(typeof(ValueTypeGetterDelegate)); return o => { var to = (T)o; - return (object)@delegate(ref to); + return (object?)@delegate(ref to); }; } else { - var @delegate = (Func) property.GetMethod.CreateDelegate(typeof(Func)); - return o => (object) @delegate((T) o); + var @delegate = (Func) property.GetMethod!.CreateDelegate(typeof(Func)); + return o => (object?) @delegate((T) o); } } } diff --git a/source/Handlebars/MemberAliasProvider/CollectionMemberAliasProvider.cs b/source/Handlebars/MemberAliasProvider/CollectionMemberAliasProvider.cs index 957a1a19..84336f1c 100644 --- a/source/Handlebars/MemberAliasProvider/CollectionMemberAliasProvider.cs +++ b/source/Handlebars/MemberAliasProvider/CollectionMemberAliasProvider.cs @@ -11,7 +11,7 @@ internal sealed class CollectionMemberAliasProvider : IMemberAliasProvider private static readonly ChainSegment Count = ChainSegment.Create("Count"); private static readonly ChainSegment Length = ChainSegment.Create("Length"); - public bool TryGetMemberByAlias(object instance, Type targetType, ChainSegment memberAlias, out object value) + public bool TryGetMemberByAlias(object instance, Type targetType, ChainSegment memberAlias, out object? value) { switch (instance) { @@ -23,7 +23,7 @@ public bool TryGetMemberByAlias(object instance, Type targetType, ChainSegment m value = array.Count; return true; - case IEnumerable enumerable when ObjectDescriptorFactory.Current.TryGetDescriptor(targetType, out var descriptor) && descriptor.GetProperties != null: + case IEnumerable enumerable when ObjectDescriptorFactory.Current!.TryGetDescriptor(targetType, out var descriptor) && descriptor.GetProperties != null: var properties = descriptor.GetProperties(descriptor, enumerable); var property = properties.OfType().FirstOrDefault(o => { diff --git a/source/Handlebars/MemberAliasProvider/DelegatedMemberAliasProvider.cs b/source/Handlebars/MemberAliasProvider/DelegatedMemberAliasProvider.cs index f15de851..26cca97c 100644 --- a/source/Handlebars/MemberAliasProvider/DelegatedMemberAliasProvider.cs +++ b/source/Handlebars/MemberAliasProvider/DelegatedMemberAliasProvider.cs @@ -35,7 +35,7 @@ public DelegatedMemberAliasProvider AddAlias(string alias, Func ac } bool IMemberAliasProvider.TryGetMemberByAlias(object instance, Type targetType, ChainSegment memberAlias, - out object value) + out object? value) { if (_aliases.TryGetValue(targetType, out var aliases) && aliases.TryGetValue(memberAlias, out var accessor)) { diff --git a/source/Handlebars/MemberAliasProvider/IMemberAliasProvider.cs b/source/Handlebars/MemberAliasProvider/IMemberAliasProvider.cs index 9a83bcce..fe980302 100644 --- a/source/Handlebars/MemberAliasProvider/IMemberAliasProvider.cs +++ b/source/Handlebars/MemberAliasProvider/IMemberAliasProvider.cs @@ -8,7 +8,7 @@ namespace HandlebarsDotNet /// public interface IMemberAliasProvider { - bool TryGetMemberByAlias(T instance, Type targetType, ChainSegment memberAlias, out object value); + bool TryGetMemberByAlias(T instance, Type targetType, ChainSegment memberAlias, out object? value); } /// diff --git a/source/Handlebars/ObjectDescriptors/DictionaryObjectDescriptor.cs b/source/Handlebars/ObjectDescriptors/DictionaryObjectDescriptor.cs index 8bb5f1df..21d49acf 100644 --- a/source/Handlebars/ObjectDescriptors/DictionaryObjectDescriptor.cs +++ b/source/Handlebars/ObjectDescriptors/DictionaryObjectDescriptor.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using HandlebarsDotNet.Iterators; using HandlebarsDotNet.MemberAccessors; @@ -10,14 +11,15 @@ namespace HandlebarsDotNet.ObjectDescriptors public sealed class DictionaryObjectDescriptor : IObjectDescriptorProvider { private static readonly Type Type = typeof(IDictionary); - private static readonly MethodInfo Factory = typeof(DictionaryObjectDescriptor) - .GetMethod(nameof(ObjectDescriptorFactory), BindingFlags.Static | BindingFlags.NonPublic); + + private static readonly MethodInfo Factory = new Func(ObjectDescriptorFactory) + .GetMethodInfo().GetGenericMethodDefinition(); private static readonly DictionaryMemberAccessor DictionaryMemberAccessor = new DictionaryMemberAccessor(); private static readonly Func GetProperties = (descriptor, arg) => ((IDictionary) arg).Keys; - public bool TryGetDescriptor(Type type, out ObjectDescriptor value) + public bool TryGetDescriptor(Type type, [MaybeNullWhen(false)] out ObjectDescriptor value) { if (!Type.IsAssignableFrom(type)) { @@ -25,7 +27,7 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value) return false; } - value = (ObjectDescriptor) Factory.MakeGenericMethod(type).Invoke(null, ArrayEx.Empty()); + value = (ObjectDescriptor) Factory.MakeGenericMethod(type).Invoke(null, ArrayEx.Empty())!; return true; } diff --git a/source/Handlebars/ObjectDescriptors/DynamicObjectDescriptor.cs b/source/Handlebars/ObjectDescriptors/DynamicObjectDescriptor.cs index f9d30767..0c93ce7e 100644 --- a/source/Handlebars/ObjectDescriptors/DynamicObjectDescriptor.cs +++ b/source/Handlebars/ObjectDescriptors/DynamicObjectDescriptor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq; using System.Linq.Expressions; @@ -33,7 +34,7 @@ public DynamicObjectDescriptor(ObjectDescriptorProvider objectDescriptorProvider _objectDescriptorProvider = objectDescriptorProvider; } - public bool TryGetDescriptor(Type type, out ObjectDescriptor value) + public bool TryGetDescriptor(Type type, [MaybeNullWhen(false)] out ObjectDescriptor value) { if (!Type.IsAssignableFrom(type)) { diff --git a/source/Handlebars/ObjectDescriptors/EnumerableObjectDescriptor.cs b/source/Handlebars/ObjectDescriptors/EnumerableObjectDescriptor.cs index eaf980d2..6face1c9 100644 --- a/source/Handlebars/ObjectDescriptors/EnumerableObjectDescriptor.cs +++ b/source/Handlebars/ObjectDescriptors/EnumerableObjectDescriptor.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using HandlebarsDotNet.Iterators; using HandlebarsDotNet.MemberAccessors; @@ -12,35 +13,42 @@ public sealed class EnumerableObjectDescriptor : IObjectDescriptorProvider { private static readonly Type Type = typeof(IEnumerable); private static readonly Type StringType = typeof(string); - private static readonly Type EnumerableObjectDescriptorType = typeof(EnumerableObjectDescriptor); - private static readonly BindingFlags BindingFlags = BindingFlags.NonPublic | BindingFlags.Static; - private static readonly MethodInfo ArrayObjectDescriptorFactoryMethodInfo = EnumerableObjectDescriptorType - .GetMethod(nameof(ArrayObjectDescriptorFactory), BindingFlags); + private static readonly MethodInfo ArrayObjectDescriptorFactoryMethodInfo = + new Func(ArrayObjectDescriptorFactory) + .GetMethodInfo().GetGenericMethodDefinition(); - private static readonly MethodInfo ListObjectDescriptorFactoryMethodInfo = EnumerableObjectDescriptorType - .GetMethod(nameof(ListObjectDescriptorFactory), BindingFlags); + private static readonly MethodInfo ListObjectDescriptorFactoryMethodInfo = + new Func(ListObjectDescriptorFactory, object>) + .GetMethodInfo().GetGenericMethodDefinition(); - private static readonly MethodInfo ReadOnlyListObjectDescriptorFactoryMethodInfo = EnumerableObjectDescriptorType - .GetMethod(nameof(ReadOnlyListObjectDescriptorFactory), BindingFlags); + private static readonly MethodInfo ReadOnlyListObjectDescriptorFactoryMethodInfo = + new Func(ReadOnlyListObjectDescriptorFactory, object>) + .GetMethodInfo().GetGenericMethodDefinition(); - private static readonly MethodInfo NonGenericListObjectDescriptorFactoryMethodInfo = EnumerableObjectDescriptorType - .GetMethod(nameof(NonGenericListObjectDescriptorFactory), BindingFlags); + private static readonly MethodInfo NonGenericListObjectDescriptorFactoryMethodInfo = + new Func(NonGenericListObjectDescriptorFactory) + .GetMethodInfo().GetGenericMethodDefinition(); - private static readonly MethodInfo CollectionObjectDescriptorFactoryMethodInfo = EnumerableObjectDescriptorType - .GetMethod(nameof(CollectionObjectDescriptorFactory), BindingFlags); + private static readonly MethodInfo CollectionObjectDescriptorFactoryMethodInfo = + new Func(CollectionObjectDescriptorFactory, object>) + .GetMethodInfo().GetGenericMethodDefinition(); - private static readonly MethodInfo ReadOnlyCollectionObjectDescriptorFactoryMethodInfo = EnumerableObjectDescriptorType - .GetMethod(nameof(ReadOnlyCollectionObjectDescriptorFactory), BindingFlags); + private static readonly MethodInfo ReadOnlyCollectionObjectDescriptorFactoryMethodInfo = + new Func(ReadOnlyCollectionObjectDescriptorFactory, object>) + .GetMethodInfo().GetGenericMethodDefinition(); - private static readonly MethodInfo NonGenericCollectionObjectDescriptorFactoryMethodInfo = EnumerableObjectDescriptorType - .GetMethod(nameof(NonGenericCollectionObjectDescriptorFactory), BindingFlags); + private static readonly MethodInfo NonGenericCollectionObjectDescriptorFactoryMethodInfo = + new Func(NonGenericCollectionObjectDescriptorFactory) + .GetMethodInfo().GetGenericMethodDefinition(); - private static readonly MethodInfo EnumerableObjectDescriptorFactoryMethodInfo = EnumerableObjectDescriptorType - .GetMethod(nameof(EnumerableObjectDescriptorFactory), BindingFlags); + private static readonly MethodInfo EnumerableObjectDescriptorFactoryMethodInfo = + new Func(EnumerableObjectDescriptorFactory, object>) + .GetMethodInfo().GetGenericMethodDefinition(); - private static readonly MethodInfo NonGenericEnumerableObjectDescriptorFactoryMethodInfo = EnumerableObjectDescriptorType - .GetMethod(nameof(NonGenericEnumerableObjectDescriptorFactory), BindingFlags); + private static readonly MethodInfo NonGenericEnumerableObjectDescriptorFactoryMethodInfo = + new Func(NonGenericEnumerableObjectDescriptorFactory) + .GetMethodInfo().GetGenericMethodDefinition(); private readonly ObjectDescriptorProvider _descriptorProvider; @@ -49,7 +57,7 @@ public EnumerableObjectDescriptor(ObjectDescriptorProvider descriptorProvider) _descriptorProvider = descriptorProvider; } - public bool TryGetDescriptor(Type type, out ObjectDescriptor value) + public bool TryGetDescriptor(Type type, [MaybeNullWhen(false)] out ObjectDescriptor value) { if (!(type != StringType && Type.IsAssignableFrom(type))) { @@ -78,13 +86,13 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value) || TryCreateDescriptor(type, typeof(IEnumerable), parameters, NonGenericEnumerableObjectDescriptorFactoryMethodInfo, out value); } - private static bool TryCreateArrayDescriptor(Type type, object[] parameters, out ObjectDescriptor value) + private static bool TryCreateArrayDescriptor(Type type, object[] parameters, [MaybeNullWhen(false)] out ObjectDescriptor value) { if (type.IsArray) { value = (ObjectDescriptor) ArrayObjectDescriptorFactoryMethodInfo - .MakeGenericMethod(type.GetElementType()) - .Invoke(null, parameters); + .MakeGenericMethod(type.GetElementType()!) + .Invoke(null, parameters)!; return true; } @@ -93,13 +101,13 @@ private static bool TryCreateArrayDescriptor(Type type, object[] parameters, out return false; } - private static bool TryCreateDescriptorFromOpenGeneric(Type type, Type openGenericType, object[] parameters, MethodInfo method, out ObjectDescriptor descriptor) + private static bool TryCreateDescriptorFromOpenGeneric(Type type, Type openGenericType, object[] parameters, MethodInfo method, [MaybeNullWhen(false)] out ObjectDescriptor descriptor) { if (type.IsAssignableToGenericType(openGenericType, out var genericType)) { descriptor = (ObjectDescriptor) method .MakeGenericMethod(type, genericType.GenericTypeArguments[0]) - .Invoke(null, parameters); + .Invoke(null, parameters)!; return true; } @@ -108,13 +116,13 @@ private static bool TryCreateDescriptorFromOpenGeneric(Type type, Type openGener return false; } - private static bool TryCreateDescriptor(Type type, Type targetType, object[] parameters, MethodInfo method, out ObjectDescriptor descriptor) + private static bool TryCreateDescriptor(Type type, Type targetType, object[] parameters, MethodInfo method, [MaybeNullWhen(false)] out ObjectDescriptor descriptor) { if (targetType.IsAssignableFrom(type)) { descriptor = (ObjectDescriptor) method .MakeGenericMethod(type) - .Invoke(null, parameters); + .Invoke(null, parameters)!; return true; } diff --git a/source/Handlebars/ObjectDescriptors/GenericDictionaryObjectDescriptorProvider.cs b/source/Handlebars/ObjectDescriptors/GenericDictionaryObjectDescriptorProvider.cs index aea98f27..5fefe608 100644 --- a/source/Handlebars/ObjectDescriptors/GenericDictionaryObjectDescriptorProvider.cs +++ b/source/Handlebars/ObjectDescriptors/GenericDictionaryObjectDescriptorProvider.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using HandlebarsDotNet.Collections; @@ -14,12 +15,13 @@ namespace HandlebarsDotNet.ObjectDescriptors { public sealed class GenericDictionaryObjectDescriptorProvider : IObjectDescriptorProvider { - private static readonly MethodInfo CreateDescriptorMethodInfo = typeof(GenericDictionaryObjectDescriptorProvider) - .GetMethod(nameof(CreateDescriptor), BindingFlags.NonPublic | BindingFlags.Static); + private static readonly MethodInfo CreateDescriptorMethodInfo = + new Func(CreateDescriptor, object, object>) + .GetMethodInfo().GetGenericMethodDefinition(); - private readonly LookupSlim, ReferenceEqualityComparer> _typeCache = new LookupSlim, ReferenceEqualityComparer>(new ReferenceEqualityComparer()); + private readonly LookupSlim, ReferenceEqualityComparer> _typeCache = new LookupSlim, ReferenceEqualityComparer>(new ReferenceEqualityComparer()); - public bool TryGetDescriptor(Type type, out ObjectDescriptor value) + public bool TryGetDescriptor(Type type, [MaybeNullWhen(false)] out ObjectDescriptor value) { var interfaceType = _typeCache.GetOrAdd(type, InterfaceTypeValueFactory).Value; if (interfaceType == null) @@ -32,14 +34,14 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value) var descriptorCreator = CreateDescriptorMethodInfo.MakeGenericMethod(type, genericArguments[0], genericArguments[1]); - value = (ObjectDescriptor) descriptorCreator.Invoke(null, ArrayEx.Empty()); + value = (ObjectDescriptor) descriptorCreator.Invoke(null, ArrayEx.Empty())!; return true; } - private static readonly Func> InterfaceTypeValueFactory = + private static readonly Func> InterfaceTypeValueFactory = key => { - return new DeferredValue(key, type => + return new DeferredValue(key, type => { return type.GetInterfaces() .Where(i => i.GetTypeInfo().IsGenericType) @@ -52,6 +54,7 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value) private static ObjectDescriptor CreateDescriptor() where T : class, IDictionary + where TK : notnull { return new ObjectDescriptor( typeof(T), diff --git a/source/Handlebars/ObjectDescriptors/IObjectDescriptorProvider.cs b/source/Handlebars/ObjectDescriptors/IObjectDescriptorProvider.cs index e21a80e6..763e0f53 100644 --- a/source/Handlebars/ObjectDescriptors/IObjectDescriptorProvider.cs +++ b/source/Handlebars/ObjectDescriptors/IObjectDescriptorProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.ObjectDescriptors { @@ -12,6 +13,6 @@ public interface IObjectDescriptorProvider /// /// /// - bool TryGetDescriptor(Type type, out ObjectDescriptor value); + bool TryGetDescriptor(Type type, [MaybeNullWhen(false)] out ObjectDescriptor value); } } \ No newline at end of file diff --git a/source/Handlebars/ObjectDescriptors/ObjectAccessor.cs b/source/Handlebars/ObjectDescriptors/ObjectAccessor.cs index daee83bb..72b69abc 100644 --- a/source/Handlebars/ObjectDescriptors/ObjectAccessor.cs +++ b/source/Handlebars/ObjectDescriptors/ObjectAccessor.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using HandlebarsDotNet.MemberAccessors; @@ -9,12 +10,12 @@ namespace HandlebarsDotNet { public readonly ref struct ObjectAccessor { - private readonly object _data; + private readonly object? _data; private readonly ObjectDescriptor _descriptor; - private readonly IMemberAccessor _memberAccessor; + private readonly IMemberAccessor? _memberAccessor; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ObjectAccessor(object data, ObjectDescriptor descriptor) + public ObjectAccessor(object? data, ObjectDescriptor descriptor) { _data = data; _descriptor = descriptor; @@ -22,12 +23,12 @@ public ObjectAccessor(object data, ObjectDescriptor descriptor) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ObjectAccessor(object data) + public ObjectAccessor(object? data) { _data = data; - if (data == null || !ObjectDescriptorFactory.Current.TryGetDescriptor(data.GetType(), out _descriptor)) + if (data == null || !ObjectDescriptorFactory.Current!.TryGetDescriptor(data.GetType(), out _descriptor)) { - _descriptor = ObjectDescriptor.Empty; + _descriptor = ObjectDescriptor.Empty!; _memberAccessor = null; } else @@ -37,17 +38,17 @@ public ObjectAccessor(object data) } public IEnumerable Properties => _descriptor - .GetProperties(_descriptor, _data) + .GetProperties(_descriptor, _data!) .OfType() .Select(ChainSegment.Create); - public object this[ChainSegment segment] => - _memberAccessor != null && _memberAccessor.TryGetValue(_data, segment, out var value) + public object? this[ChainSegment segment] => + _memberAccessor != null && _memberAccessor.TryGetValue(_data!, segment, out var value) ? value : null; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetValue(ChainSegment segment, out object value) + public bool TryGetValue(ChainSegment segment, out object? value) { if (_memberAccessor == null) { @@ -55,7 +56,7 @@ public bool TryGetValue(ChainSegment segment, out object value) return false; } - return _memberAccessor.TryGetValue(_data, segment, out value); + return _memberAccessor.TryGetValue(_data!, segment, out value); } } } \ No newline at end of file diff --git a/source/Handlebars/ObjectDescriptors/ObjectDescriptor.cs b/source/Handlebars/ObjectDescriptors/ObjectDescriptor.cs index 5160cb86..bf91ae56 100644 --- a/source/Handlebars/ObjectDescriptors/ObjectDescriptor.cs +++ b/source/Handlebars/ObjectDescriptors/ObjectDescriptor.cs @@ -1,5 +1,7 @@ using System; using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using HandlebarsDotNet.Iterators; using HandlebarsDotNet.MemberAccessors; @@ -14,27 +16,27 @@ public sealed class ObjectDescriptor public static readonly ObjectDescriptor Empty = new ObjectDescriptor(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ObjectDescriptor Create(object from) + public static ObjectDescriptor Create(object? from) { return Create(from?.GetType()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ObjectDescriptor Create(Type from) + public static ObjectDescriptor Create(Type? from) { - if (from == null) return Empty; - if (!ObjectDescriptorFactory.Current.TryGetDescriptor(@from, out var descriptor)) return Empty; + if (from == null) return Empty!; + if (!ObjectDescriptorFactory.Current!.TryGetDescriptor(@from, out var descriptor)) return Empty!; return descriptor; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryCreate(object from, out ObjectDescriptor descriptor) + public static bool TryCreate(object? from, out ObjectDescriptor descriptor) { return TryCreate(from?.GetType(), out descriptor); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryCreate(Type @from, out ObjectDescriptor descriptor) + public static bool TryCreate(Type? @from, out ObjectDescriptor descriptor) { if (from == null) { @@ -42,7 +44,7 @@ public static bool TryCreate(Type @from, out ObjectDescriptor descriptor) return false; } - return ObjectDescriptorFactory.Current.TryGetDescriptor(from, out descriptor); + return ObjectDescriptorFactory.Current!.TryGetDescriptor(from, out descriptor); } /// @@ -68,12 +70,19 @@ params object[] dependencies Iterator = iterator(this); } - private ObjectDescriptor(){ } + private ObjectDescriptor() + { + DescribedType = null!; + GetProperties = null!; + MemberAccessor = null!; + Dependencies = null!; + Iterator = null!; + } /// /// Iterator implementation for /// - public readonly IIterator Iterator; + public readonly IIterator? Iterator; /// /// Contains dependencies for delegate diff --git a/source/Handlebars/ObjectDescriptors/ObjectDescriptorFactory.cs b/source/Handlebars/ObjectDescriptors/ObjectDescriptorFactory.cs index a803f5f1..9ccd6404 100644 --- a/source/Handlebars/ObjectDescriptors/ObjectDescriptorFactory.cs +++ b/source/Handlebars/ObjectDescriptors/ObjectDescriptorFactory.cs @@ -1,11 +1,12 @@ using System; +using System.Diagnostics.CodeAnalysis; using HandlebarsDotNet.Collections; using HandlebarsDotNet.EqualityComparers; using HandlebarsDotNet.Runtime; namespace HandlebarsDotNet.ObjectDescriptors { - public class ObjectDescriptorFactory : IObjectDescriptorProvider, IObserver> + public class ObjectDescriptorFactory : IObjectDescriptorProvider, IObserver> { private readonly ObservableList _providers; private readonly LookupSlim, ReferenceEqualityComparer> _descriptorsCache = new LookupSlim, ReferenceEqualityComparer>(new ReferenceEqualityComparer()); @@ -19,20 +20,20 @@ public class ObjectDescriptorFactory : IObjectDescriptorProvider, IObserver> _observer; + private readonly IObserver> _observer; - public static ObjectDescriptorFactory Current => AmbientContext.Current?.ObjectDescriptorFactory; + public static ObjectDescriptorFactory? Current => AmbientContext.Current?.ObjectDescriptorFactory; - public ObjectDescriptorFactory(ObservableList providers = null) + public ObjectDescriptorFactory(ObservableList? providers = null) { _providers = new ObservableList(); if (providers != null) Append(providers); - _observer = ObserverBuilder>.Create(_descriptorsCache) + _observer = ObserverBuilder>.Create(_descriptorsCache) .OnEvent>((@event, state) => state.Clear()) .Build(); @@ -65,6 +66,6 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value) public void OnError(Exception error) => _observer.OnError(error); - public void OnNext(ObservableEvent value) => _observer.OnNext(value); + public void OnNext(IObservableEvent value) => _observer.OnNext(value); } } \ No newline at end of file diff --git a/source/Handlebars/ObjectDescriptors/ObjectDescriptorProvider.cs b/source/Handlebars/ObjectDescriptors/ObjectDescriptorProvider.cs index f377d631..1304d1aa 100644 --- a/source/Handlebars/ObjectDescriptors/ObjectDescriptorProvider.cs +++ b/source/Handlebars/ObjectDescriptors/ObjectDescriptorProvider.cs @@ -33,13 +33,7 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value) return true; } - - private static readonly Func> GetProperties = (descriptor, o) => - { - var cache = (LookupSlim, ReferenceEqualityComparer>) descriptor.Dependencies[0]; - return cache.GetOrAdd(descriptor.DescribedType, DescriptorValueFactory).Value; - }; - + private static readonly Func> DescriptorValueFactory = key => { @@ -57,5 +51,11 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value) .ToArray(); }); }; + + private static readonly Func> GetProperties = (descriptor, o) => + { + var cache = (LookupSlim, ReferenceEqualityComparer>) descriptor.Dependencies[0]; + return cache.GetOrAdd(descriptor.DescribedType, DescriptorValueFactory).Value; + }; } } \ No newline at end of file diff --git a/source/Handlebars/ObjectDescriptors/ReadOnlyGenericDictionaryObjectDescriptorProvider.cs b/source/Handlebars/ObjectDescriptors/ReadOnlyGenericDictionaryObjectDescriptorProvider.cs index 963f2b06..d79d76be 100644 --- a/source/Handlebars/ObjectDescriptors/ReadOnlyGenericDictionaryObjectDescriptorProvider.cs +++ b/source/Handlebars/ObjectDescriptors/ReadOnlyGenericDictionaryObjectDescriptorProvider.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using HandlebarsDotNet.Collections; @@ -14,12 +15,13 @@ namespace HandlebarsDotNet.ObjectDescriptors { public sealed class ReadOnlyGenericDictionaryObjectDescriptorProvider : IObjectDescriptorProvider { - private static readonly MethodInfo CreateDescriptorMethodInfo = typeof(ReadOnlyGenericDictionaryObjectDescriptorProvider) - .GetMethod(nameof(CreateDescriptor), BindingFlags.NonPublic | BindingFlags.Static); + private static readonly MethodInfo CreateDescriptorMethodInfo = + new Func(CreateDescriptor, object, object>) + .GetMethodInfo().GetGenericMethodDefinition(); - private readonly LookupSlim, ReferenceEqualityComparer> _typeCache = new LookupSlim, ReferenceEqualityComparer>(new ReferenceEqualityComparer()); + private readonly LookupSlim, ReferenceEqualityComparer> _typeCache = new LookupSlim, ReferenceEqualityComparer>(new ReferenceEqualityComparer()); - public bool TryGetDescriptor(Type type, out ObjectDescriptor value) + public bool TryGetDescriptor(Type type, [MaybeNullWhen(false)] out ObjectDescriptor value) { var interfaceType = _typeCache.GetOrAdd(type, InterfaceTypeValueFactory).Value; if (interfaceType == null) @@ -32,14 +34,14 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value) var descriptorCreator = CreateDescriptorMethodInfo.MakeGenericMethod(type, genericArguments[0], genericArguments[1]); - value = (ObjectDescriptor) descriptorCreator.Invoke(null, ArrayEx.Empty()); + value = (ObjectDescriptor) descriptorCreator.Invoke(null, ArrayEx.Empty())!; return true; } - private static readonly Func> InterfaceTypeValueFactory = + private static readonly Func> InterfaceTypeValueFactory = key => { - return new DeferredValue(key, type => + return new DeferredValue(key, type => { return type.GetInterfaces() .Where(i => IntrospectionExtensions.GetTypeInfo(i).IsGenericType) @@ -52,6 +54,7 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value) private static ObjectDescriptor CreateDescriptor() where T : class, IReadOnlyDictionary + where TK : notnull { return new ObjectDescriptor( typeof(T), diff --git a/source/Handlebars/ObjectDescriptors/ReadOnlyStringDictionaryObjectDescriptorProvider.cs b/source/Handlebars/ObjectDescriptors/ReadOnlyStringDictionaryObjectDescriptorProvider.cs index c702e7a6..c5444497 100644 --- a/source/Handlebars/ObjectDescriptors/ReadOnlyStringDictionaryObjectDescriptorProvider.cs +++ b/source/Handlebars/ObjectDescriptors/ReadOnlyStringDictionaryObjectDescriptorProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using HandlebarsDotNet.Collections; @@ -14,12 +15,13 @@ namespace HandlebarsDotNet.ObjectDescriptors public sealed class ReadOnlyStringDictionaryObjectDescriptorProvider : IObjectDescriptorProvider { private static readonly object[] EmptyArray = ArrayEx.Empty(); - private static readonly MethodInfo CreateClassDescriptorMethodInfo = typeof(ReadOnlyStringDictionaryObjectDescriptorProvider) - .GetMethod(nameof(CreateDescriptor), BindingFlags.NonPublic | BindingFlags.Static); + private static readonly MethodInfo CreateClassDescriptorMethodInfo = + new Func(CreateDescriptor, object>) + .GetMethodInfo().GetGenericMethodDefinition(); - private readonly LookupSlim, ReferenceEqualityComparer> _typeCache = new LookupSlim, ReferenceEqualityComparer>(new ReferenceEqualityComparer()); + private readonly LookupSlim, ReferenceEqualityComparer> _typeCache = new LookupSlim, ReferenceEqualityComparer>(new ReferenceEqualityComparer()); - public bool TryGetDescriptor(Type type, out ObjectDescriptor value) + public bool TryGetDescriptor(Type type, [MaybeNullWhen(false)] out ObjectDescriptor value) { var interfaceType = _typeCache.GetOrAdd(type, InterfaceTypeValueFactory).Value; if (interfaceType == null) @@ -34,12 +36,12 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value) var descriptorCreator = factory .MakeGenericMethod(type, typeArgument); - value = (ObjectDescriptor) descriptorCreator.Invoke(null, EmptyArray); + value = (ObjectDescriptor) descriptorCreator.Invoke(null, EmptyArray)!; return true; } - private static readonly Func> InterfaceTypeValueFactory = - key => new DeferredValue(key, type => + private static readonly Func> InterfaceTypeValueFactory = + key => new DeferredValue(key, type => { return type.GetInterfaces() .FirstOrDefault(i => diff --git a/source/Handlebars/ObjectDescriptors/StringDictionaryObjectDescriptorProvider.cs b/source/Handlebars/ObjectDescriptors/StringDictionaryObjectDescriptorProvider.cs index b4c1a98b..cd1163b9 100644 --- a/source/Handlebars/ObjectDescriptors/StringDictionaryObjectDescriptorProvider.cs +++ b/source/Handlebars/ObjectDescriptors/StringDictionaryObjectDescriptorProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using HandlebarsDotNet.Collections; @@ -14,12 +15,13 @@ namespace HandlebarsDotNet.ObjectDescriptors public sealed class StringDictionaryObjectDescriptorProvider : IObjectDescriptorProvider { private static readonly object[] EmptyArray = ArrayEx.Empty(); - private static readonly MethodInfo CreateClassDescriptorMethodInfo = typeof(StringDictionaryObjectDescriptorProvider) - .GetMethod(nameof(CreateDescriptor), BindingFlags.NonPublic | BindingFlags.Static); + private static readonly MethodInfo CreateClassDescriptorMethodInfo = + new Func(CreateDescriptor, object>) + .GetMethodInfo().GetGenericMethodDefinition(); - private readonly LookupSlim, ReferenceEqualityComparer> _typeCache = new LookupSlim, ReferenceEqualityComparer>(new ReferenceEqualityComparer()); + private readonly LookupSlim, ReferenceEqualityComparer> _typeCache = new LookupSlim, ReferenceEqualityComparer>(new ReferenceEqualityComparer()); - public bool TryGetDescriptor(Type type, out ObjectDescriptor value) + public bool TryGetDescriptor(Type type, [MaybeNullWhen(false)] out ObjectDescriptor value) { var interfaceType = _typeCache.GetOrAdd(type, InterfaceTypeValueFactory).Value; if (interfaceType == null) @@ -34,12 +36,12 @@ public bool TryGetDescriptor(Type type, out ObjectDescriptor value) var descriptorCreator = factory .MakeGenericMethod(type, typeArgument); - value = (ObjectDescriptor) descriptorCreator.Invoke(null, EmptyArray); + value = (ObjectDescriptor) descriptorCreator.Invoke(null, EmptyArray)!; return true; } - private static readonly Func> InterfaceTypeValueFactory = - key => new DeferredValue(key, type => + private static readonly Func> InterfaceTypeValueFactory = + key => new DeferredValue(key, type => { return type.GetInterfaces() .FirstOrDefault(i => diff --git a/source/Handlebars/PathInfoLight.cs b/source/Handlebars/PathInfoLight.cs index 02f71e5a..8d443aa2 100644 --- a/source/Handlebars/PathInfoLight.cs +++ b/source/Handlebars/PathInfoLight.cs @@ -43,12 +43,12 @@ public bool Equals(PathInfoLight other) return _comparerTag == other._comparerTag && Equals(PathInfo, other.PathInfo); } - public bool Equals(PathInfo other) + public bool Equals(PathInfo? other) { return Equals(PathInfo, other); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is PathInfoLight other && Equals(other); } diff --git a/source/Handlebars/PathStructure/ChainSegment.cs b/source/Handlebars/PathStructure/ChainSegment.cs index 445a944e..4fa819aa 100644 --- a/source/Handlebars/PathStructure/ChainSegment.cs +++ b/source/Handlebars/PathStructure/ChainSegment.cs @@ -25,7 +25,7 @@ public sealed partial class ChainSegment : IEquatable public static ChainSegment Create(object value) { if (value is ChainSegment segment) return segment; - var sValue = value as string ?? value.ToString(); + var sValue = value as string ?? value.ToString()!; return ChainSegmentStore.Current?.Create(sValue) ?? new ChainSegment(sValue); } @@ -99,7 +99,7 @@ internal ChainSegment(string value, WellKnownVariable wellKnownVariable = WellKn public override string ToString() => _value; /// - public bool Equals(ChainSegment other) + public bool Equals(ChainSegment? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; @@ -107,7 +107,7 @@ public bool Equals(ChainSegment other) } /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; @@ -137,10 +137,10 @@ private int GetHashCodeImpl() } /// - public static bool operator ==(ChainSegment a, ChainSegment b) => Equals(a, b); + public static bool operator ==(ChainSegment? a, ChainSegment? b) => Equals(a, b); /// - public static bool operator !=(ChainSegment a, ChainSegment b) => !Equals(a, b); + public static bool operator !=(ChainSegment? a, ChainSegment? b) => !Equals(a, b); /// public static implicit operator string(ChainSegment segment) => segment._value; diff --git a/source/Handlebars/PathStructure/ChainSegmentStore.cs b/source/Handlebars/PathStructure/ChainSegmentStore.cs index 7fc94358..43700b52 100644 --- a/source/Handlebars/PathStructure/ChainSegmentStore.cs +++ b/source/Handlebars/PathStructure/ChainSegmentStore.cs @@ -13,7 +13,7 @@ public sealed class ChainSegmentStore return new DeferredValue(new CreationProperties(s, v), properties => new ChainSegment(properties.String, properties.KnownVariable)); }; - public static ChainSegmentStore Current => AmbientContext.Current?.ChainSegmentStore; + public static ChainSegmentStore? Current => AmbientContext.Current?.ChainSegmentStore; private readonly LookupSlim, StringEqualityComparer> _lookup = new LookupSlim, StringEqualityComparer>(new StringEqualityComparer(StringComparison.Ordinal)); @@ -29,7 +29,7 @@ internal ChainSegmentStore() public ChainSegment Create(object value) { if (value is ChainSegment segment) return segment; - return _lookup.GetOrAdd(value as string ?? value.ToString(), ValueFactory, WellKnownVariable.None).Value; + return _lookup.GetOrAdd(value as string ?? value.ToString()!, ValueFactory, WellKnownVariable.None).Value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/source/Handlebars/PathStructure/PathInfo.cs b/source/Handlebars/PathStructure/PathInfo.cs index 6acbfba8..57b6b90e 100644 --- a/source/Handlebars/PathStructure/PathInfo.cs +++ b/source/Handlebars/PathStructure/PathInfo.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using HandlebarsDotNet.Collections; @@ -26,19 +27,31 @@ public enum PathType public sealed partial class PathInfo : IEquatable { internal readonly bool IsValidHelperLiteral; - internal readonly bool HasValue; - internal readonly bool IsThis; - internal readonly bool IsPureThis; - internal readonly bool IsInversion; - internal readonly bool IsBlockHelper; - internal readonly bool IsBlockClose; + + [MemberNotNullWhen(true, nameof(Segments), nameof(TrimmedPath))] + internal bool HasValue { get; } + + [MemberNotNullWhen(true, nameof(Segments), nameof(TrimmedPath))] + internal bool IsThis { get; } + + [MemberNotNullWhen(true, nameof(Segments), nameof(TrimmedPath))] + internal bool IsPureThis { get; } + + [MemberNotNullWhen(true, nameof(Segments), nameof(TrimmedPath))] + internal bool IsInversion { get; } + + [MemberNotNullWhen(true, nameof(Segments), nameof(TrimmedPath))] + internal bool IsBlockHelper { get; } + + [MemberNotNullWhen(true, nameof(Segments), nameof(TrimmedPath))] + internal bool IsBlockClose { get; } private readonly int _hashCode; private readonly int _trimmedHashCode; private readonly int _trimmedInvariantHashCode; public static readonly PathInfo Empty = new PathInfo(PathType.Empty, "null", false, ArrayEx.Empty()); - + private PathInfo( PathType pathType, string path, @@ -90,13 +103,14 @@ private PathInfo( /// /// Indicates whether is part of @ variable /// + // [MemberNotNullWhen(true, nameof(Segments), nameof(TrimmedPath))] public readonly bool IsVariable; - public readonly PathSegment[] Segments; + public readonly PathSegment[]? Segments; public readonly string Path; - public readonly string TrimmedPath; + public readonly string? TrimmedPath; /// - public bool Equals(PathInfo other) + public bool Equals(PathInfo? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; @@ -104,7 +118,7 @@ public bool Equals(PathInfo other) } /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; diff --git a/source/Handlebars/PathStructure/PathInfoStore.cs b/source/Handlebars/PathStructure/PathInfoStore.cs index 250983a2..3bbe832e 100644 --- a/source/Handlebars/PathStructure/PathInfoStore.cs +++ b/source/Handlebars/PathStructure/PathInfoStore.cs @@ -15,7 +15,7 @@ public class PathInfoStore }); }; - public static PathInfoStore Current => AmbientContext.Current?.PathInfoStore; + public static PathInfoStore? Current => AmbientContext.Current?.PathInfoStore; private readonly LookupSlim, StringEqualityComparer> _paths = new LookupSlim, StringEqualityComparer>(new StringEqualityComparer(StringComparison.Ordinal)); @@ -25,11 +25,10 @@ internal PathInfoStore(){} public PathInfo GetOrAdd(string path) { var pathInfo = _paths.GetOrAdd(path, ValueFactory).Value; - - var trimmedPath = pathInfo.TrimmedPath; + if (pathInfo.IsBlockHelper || pathInfo.IsInversion) { - _paths.GetOrAdd(trimmedPath, ValueFactory); + _paths.GetOrAdd(pathInfo.TrimmedPath, ValueFactory); } return pathInfo; diff --git a/source/Handlebars/PathStructure/PathResolver.cs b/source/Handlebars/PathStructure/PathResolver.cs index 6730ec0f..9c500610 100644 --- a/source/Handlebars/PathStructure/PathResolver.cs +++ b/source/Handlebars/PathStructure/PathResolver.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using HandlebarsDotNet.Compiler; @@ -5,13 +6,13 @@ namespace HandlebarsDotNet.PathStructure { public static class PathResolver { - public static object ResolvePath(BindingContext context, PathInfo pathInfo) + public static object? ResolvePath(BindingContext context, PathInfo pathInfo) { if (!pathInfo.HasValue) return null; if (pathInfo.IsPureThis) return context.Value; var instance = context.Value; - var throwOnUnresolvedBindingExpression = context!.Configuration.ThrowOnUnresolvedBindingExpression; + var throwOnUnresolvedBindingExpression = context.Configuration.ThrowOnUnresolvedBindingExpression; var segments = pathInfo.Segments; for (var segmentIndex = 0; segmentIndex < segments.Length; segmentIndex++) @@ -20,8 +21,8 @@ public static object ResolvePath(BindingContext context, PathInfo pathInfo) if (segment.IsThis) continue; if (segment.IsParent) { - context = context!.ParentContext; - if (context == null) + context = context.ParentContext!; + if (context == null!) { instance = UndefinedBindingResult.Create(".."); goto undefined; @@ -33,7 +34,7 @@ public static object ResolvePath(BindingContext context, PathInfo pathInfo) } var pathChain = segment.PathChain; - if (!TryResolveValue(pathInfo.IsVariable, context, pathChain[0], instance, out instance)) + if (!TryResolveValue(pathInfo.IsVariable, context, pathChain[0], instance!, out instance)) { instance = UndefinedBindingResult.Create(pathChain[0]); goto undefined; @@ -59,7 +60,7 @@ public static object ResolvePath(BindingContext context, PathInfo pathInfo) return instance; } - private static bool TryResolveValue(bool isVariable, BindingContext context, ChainSegment chainSegment, object instance, out object value) + private static bool TryResolveValue(bool isVariable, BindingContext context, ChainSegment chainSegment, object instance, out object? value) { if (isVariable) { @@ -82,7 +83,7 @@ private static bool TryResolveValue(bool isVariable, BindingContext context, Cha } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryAccessMember(BindingContext context, object instance, ChainSegment chainSegment, out object value) + public static bool TryAccessMember(BindingContext context, object? instance, ChainSegment chainSegment, [MaybeNullWhen(false)] out object value) { if (instance == null) { @@ -107,6 +108,7 @@ private static ChainSegment ResolveMemberName(object instance, ChainSegment memb private static class Throw { [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] public static void Undefined(PathInfo pathInfo, UndefinedBindingResult undefinedBindingResult) => throw new HandlebarsUndefinedBindingException(pathInfo, undefinedBindingResult); } } diff --git a/source/Handlebars/PathStructure/PathSegment.cs b/source/Handlebars/PathStructure/PathSegment.cs index 5fcbe739..7f1a45ec 100644 --- a/source/Handlebars/PathStructure/PathSegment.cs +++ b/source/Handlebars/PathStructure/PathSegment.cs @@ -47,12 +47,12 @@ public bool Equals(PathSegment other) return true; } - public bool Equals(string other) + public bool Equals(string? other) { return string.Equals(other, ToString(), StringComparison.OrdinalIgnoreCase); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is PathSegment other && Equals(other); } diff --git a/source/Handlebars/Polyfills/AsyncLocal.cs b/source/Handlebars/Polyfills/AsyncLocal.cs index 66726133..61f6f78a 100644 --- a/source/Handlebars/Polyfills/AsyncLocal.cs +++ b/source/Handlebars/Polyfills/AsyncLocal.cs @@ -1,5 +1,6 @@ #if NET451 using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.Remoting.Messaging; @@ -9,6 +10,7 @@ public sealed class AsyncLocal { private const string Slot = "__AsyncLocalSlot"; + [MaybeNull] public T Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/source/Handlebars/Polyfills/NullableAttributes.cs b/source/Handlebars/Polyfills/NullableAttributes.cs new file mode 100644 index 00000000..7d71bd0c --- /dev/null +++ b/source/Handlebars/Polyfills/NullableAttributes.cs @@ -0,0 +1,144 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics.CodeAnalysis +{ + +#if !NET6_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class DisallowNullAttribute : Attribute { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute { } + + /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class NotNullAttribute : Attribute { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + internal sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullAttribute : Attribute + { + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } + } +#endif + +#if !NET6_0_OR_GREATER + /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new[] { member }; + } + + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } + } +#endif +} \ No newline at end of file diff --git a/source/Handlebars/Pools/BindingContext.Pool.cs b/source/Handlebars/Pools/BindingContext.Pool.cs index 70458426..e2c4ada6 100644 --- a/source/Handlebars/Pools/BindingContext.Pool.cs +++ b/source/Handlebars/Pools/BindingContext.Pool.cs @@ -7,13 +7,13 @@ public sealed partial class BindingContext { private static readonly BindingContextPool Pool = new BindingContextPool(); - internal static BindingContext Create(ICompiledHandlebarsConfiguration configuration, object value) + internal static BindingContext Create(ICompiledHandlebarsConfiguration configuration, object? value) { return Pool.CreateContext(configuration, value, null, null); } - internal static BindingContext Create(ICompiledHandlebarsConfiguration configuration, object value, BindingContext parent, - TemplateDelegate partialBlockTemplate) + internal static BindingContext Create(ICompiledHandlebarsConfiguration configuration, object? value, BindingContext parent, + TemplateDelegate? partialBlockTemplate) { return Pool.CreateContext(configuration, value, parent, partialBlockTemplate); } @@ -26,7 +26,7 @@ public BindingContextPool() : base(new BindingContextPolicy()) { } - public BindingContext CreateContext(ICompiledHandlebarsConfiguration configuration, object value, BindingContext parent, TemplateDelegate partialBlockTemplate) + public BindingContext CreateContext(ICompiledHandlebarsConfiguration configuration, object? value, BindingContext? parent, TemplateDelegate? partialBlockTemplate) { var context = Get(); context.Configuration = configuration; @@ -46,10 +46,10 @@ internal struct BindingContextPolicy : IInternalObjectPoolPolicy public bool Return(BindingContext item) { - item.Configuration = null; + item.Configuration = null!; - item.Root = null; - item.Value = null; + item.Root = null!; + item.Value = null!; item.ParentContext = null; item.PartialBlockTemplate = null; item.InlinePartialTemplates.Clear(); diff --git a/source/Handlebars/Runtime/AmbientContext.cs b/source/Handlebars/Runtime/AmbientContext.cs index e21c8f73..9ae47b4d 100644 --- a/source/Handlebars/Runtime/AmbientContext.cs +++ b/source/Handlebars/Runtime/AmbientContext.cs @@ -13,23 +13,23 @@ public sealed class AmbientContext : IDisposable private static readonly InternalObjectPool Pool = new(new Policy()); [ThreadStatic] - private static Stack _local; + private static Stack? _local; private static Stack Local => _local ??= new Stack(); - public static AmbientContext Current + public static AmbientContext? Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Local.Count > 0 ? Local.Peek() : null; } public static AmbientContext Create( - PathInfoStore pathInfoStore = null, - ChainSegmentStore chainSegmentStore = null, - UndefinedBindingResultCache undefinedBindingResultCache = null, - FormatterProvider formatterProvider = null, - ObjectDescriptorFactory descriptorFactory = null + PathInfoStore? pathInfoStore = null, + ChainSegmentStore? chainSegmentStore = null, + UndefinedBindingResultCache? undefinedBindingResultCache = null, + FormatterProvider? formatterProvider = null, + ObjectDescriptorFactory? descriptorFactory = null ) { var ambientContext = Pool.Get(); @@ -45,11 +45,11 @@ public static AmbientContext Create( public static AmbientContext Create( AmbientContext context, - PathInfoStore pathInfoStore = null, - ChainSegmentStore chainSegmentStore = null, - UndefinedBindingResultCache undefinedBindingResultCache = null, - FormatterProvider formatterProvider = null, - ObjectDescriptorFactory descriptorFactory = null + PathInfoStore? pathInfoStore = null, + ChainSegmentStore? chainSegmentStore = null, + UndefinedBindingResultCache? undefinedBindingResultCache = null, + FormatterProvider? formatterProvider = null, + ObjectDescriptorFactory? descriptorFactory = null ) { var ambientContext = Pool.Get(); @@ -74,15 +74,15 @@ private AmbientContext() { } - public PathInfoStore PathInfoStore { get; private set; } + public PathInfoStore PathInfoStore { get; private set; } = null!; - public ChainSegmentStore ChainSegmentStore { get; private set; } + public ChainSegmentStore ChainSegmentStore { get; private set; } = null!; - public UndefinedBindingResultCache UndefinedBindingResultCache { get; private set; } + public UndefinedBindingResultCache UndefinedBindingResultCache { get; private set; } = null!; - public FormatterProvider FormatterProvider { get; private set; } + public FormatterProvider FormatterProvider { get; private set; } = null!; - public ObjectDescriptorFactory ObjectDescriptorFactory { get; private set; } + public ObjectDescriptorFactory ObjectDescriptorFactory { get; private set; } = null!; public Dictionary Bag { get; } = new(); @@ -92,11 +92,11 @@ private struct Policy : IInternalObjectPoolPolicy public bool Return(AmbientContext item) { - item.PathInfoStore = null; - item.ChainSegmentStore = null; - item.UndefinedBindingResultCache = null; - item.FormatterProvider = null; - item.ObjectDescriptorFactory = null; + item.PathInfoStore = null!; + item.ChainSegmentStore = null!; + item.UndefinedBindingResultCache = null!; + item.FormatterProvider = null!; + item.ObjectDescriptorFactory = null!; item.Bag.Clear(); return true; diff --git a/source/Handlebars/Runtime/DeferredValue.cs b/source/Handlebars/Runtime/DeferredValue.cs index 7440f22c..c4187492 100644 --- a/source/Handlebars/Runtime/DeferredValue.cs +++ b/source/Handlebars/Runtime/DeferredValue.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; +using HandlebarsDotNet.Collections; +using HandlebarsDotNet.IO; namespace HandlebarsDotNet.Runtime { @@ -8,7 +11,7 @@ public sealed class DeferredValue private readonly TState _state; private readonly Func _factory; - private T _value; + private T? _value; private bool _isValueCreated; public DeferredValue(TState state, Func factory) @@ -17,10 +20,10 @@ public DeferredValue(TState state, Func factory) _factory = factory; } - public override string ToString() + public override string? ToString() { if (!_isValueCreated) return "Not yet created"; - return _value.ToString(); + return _value!.ToString(); } public T Value @@ -28,7 +31,7 @@ public T Value [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - if (_isValueCreated) return _value; + if (_isValueCreated) return _value!; _value = _factory(_state); _isValueCreated = true; diff --git a/source/Handlebars/Runtime/GcDeferredValue.cs b/source/Handlebars/Runtime/GcDeferredValue.cs index 7c26aec2..7da49748 100644 --- a/source/Handlebars/Runtime/GcDeferredValue.cs +++ b/source/Handlebars/Runtime/GcDeferredValue.cs @@ -8,8 +8,7 @@ public sealed class GcDeferredValue private readonly TState _state; private readonly Func _factory; - private WeakReference _value; - private bool _isValueCreated; + private WeakReference? _value; public GcDeferredValue(TState state, Func factory) { @@ -17,9 +16,9 @@ public GcDeferredValue(TState state, Func factory) _factory = factory; } - public override string ToString() + public override string? ToString() { - if (!_isValueCreated) return "Not yet created"; + if (_value == null) return "Not yet created"; if (!_value.TryGetTarget(out var value)) return "GC collected"; return value.ToString(); @@ -30,9 +29,9 @@ public T Value get { T value; - if (_isValueCreated) + if (_value != null) { - if(_value.TryGetTarget(out value)) return value; + if(_value.TryGetTarget(out value!)) return value; value = _factory(_state); _value.SetTarget(value); @@ -41,7 +40,6 @@ public T Value { value = _factory(_state); _value = new WeakReference(value); - _isValueCreated = true; } return value; diff --git a/source/Handlebars/Runtime/Ref.cs b/source/Handlebars/Runtime/Ref.cs index 26285a4e..9aa3b765 100644 --- a/source/Handlebars/Runtime/Ref.cs +++ b/source/Handlebars/Runtime/Ref.cs @@ -2,10 +2,8 @@ namespace HandlebarsDotNet.Runtime { - public sealed class Ref where T: class + public sealed class Ref where T: class? { - public Ref() { } - public Ref(T value) => Value = value; public T Value diff --git a/source/Handlebars/StringUtils/Substring.cs b/source/Handlebars/StringUtils/Substring.cs index 3211f499..a1e7787a 100644 --- a/source/Handlebars/StringUtils/Substring.cs +++ b/source/Handlebars/StringUtils/Substring.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace HandlebarsDotNet.StringUtils @@ -73,13 +74,13 @@ public bool Equals(Substring other) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(string other) + public bool Equals(string? other) { if (Length != other?.Length) return false; return string.CompareOrdinal(String, Start, other, 0, Length) == 0; } - public override bool Equals(object obj) => obj is Substring other && Equals(other); + public override bool Equals(object? obj) => obj is Substring other && Equals(other); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() @@ -160,16 +161,16 @@ public static bool EndsWith(in Substring substring, in char c) public static bool operator !=(in Substring a, in Substring b) => !a.Equals(b); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(in Substring a, string b) => a.Equals(b); + public static bool operator ==(in Substring a, string? b) => a.Equals(b); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(in Substring a, string b) => !a.Equals(b); + public static bool operator !=(in Substring a, string? b) => !a.Equals(b); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(string a, in Substring b) => b.Equals(a); + public static bool operator ==(string? a, in Substring b) => b.Equals(a); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(string a, in Substring b) => !b.Equals(a); + public static bool operator !=(string? a, in Substring b) => !b.Equals(a); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Contains(in Substring substring, in char c) @@ -315,9 +316,11 @@ public void Dispose() private static class Throw { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IndexOutOfRangeException(string message = null) => throw new ArgumentOutOfRangeException(message); + [DoesNotReturn] + public static void IndexOutOfRangeException(string? message = null) => throw new ArgumentOutOfRangeException(message); [MethodImpl(MethodImplOptions.AggressiveInlining)] + [DoesNotReturn] public static void ArgumentNullException(string argument) => throw new ArgumentNullException(argument); } } diff --git a/source/Handlebars/UndefinedBindingResult.cs b/source/Handlebars/UndefinedBindingResult.cs index ae5adb14..32d43968 100644 --- a/source/Handlebars/UndefinedBindingResult.cs +++ b/source/Handlebars/UndefinedBindingResult.cs @@ -16,7 +16,7 @@ public sealed class UndefinedBindingResultCache private readonly LookupSlim, StringEqualityComparer> _cache = new LookupSlim, StringEqualityComparer>(new StringEqualityComparer(StringComparison.Ordinal)); - public static UndefinedBindingResultCache Current => AmbientContext.Current?.UndefinedBindingResultCache; + public static UndefinedBindingResultCache? Current => AmbientContext.Current?.UndefinedBindingResultCache; internal UndefinedBindingResultCache() { @@ -37,9 +37,9 @@ public sealed class UndefinedBindingResult : IEquatable public override string ToString() => Value; - public bool Equals(UndefinedBindingResult other) => Value == other?.Value; + public bool Equals(UndefinedBindingResult? other) => Value == other?.Value; - public override bool Equals(object obj) => obj is UndefinedBindingResult other && Equals(other); + public override bool Equals(object? obj) => obj is UndefinedBindingResult other && Equals(other); public override int GetHashCode() => Value != null ? Value.GetHashCode() : 0; } diff --git a/source/Handlebars/ValueProviders/BlockParamsValues.cs b/source/Handlebars/ValueProviders/BlockParamsValues.cs index a5937cd0..beeead64 100644 --- a/source/Handlebars/ValueProviders/BlockParamsValues.cs +++ b/source/Handlebars/ValueProviders/BlockParamsValues.cs @@ -7,10 +7,10 @@ namespace HandlebarsDotNet.ValueProviders public readonly ref struct BlockParamsValues { private readonly ChainSegment[] _variables; - private readonly FixedSizeDictionary _values; + private readonly FixedSizeDictionary? _values; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BlockParamsValues(BindingContext context, ChainSegment[] variables) + public BlockParamsValues(BindingContext? context, ChainSegment[] variables) { _variables = variables; _values = context?.BlockParamsObject; @@ -27,10 +27,10 @@ public void CreateProperty(in int variableIndex, out EntryIndex in } var value = UndefinedBindingResult.Create(variable); - _values.AddOrReplace(variable, value, out index); + _values!.AddOrReplace(variable, value, out index); } - public object this[in EntryIndex index] + public object? this[in EntryIndex index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] set @@ -40,7 +40,7 @@ public object this[in EntryIndex index] } } - public object this[int variableIndex] + public object? this[int variableIndex] { [MethodImpl(MethodImplOptions.AggressiveInlining)] set @@ -52,7 +52,7 @@ public object this[int variableIndex] } } - private ChainSegment GetVariable(int index) + private ChainSegment? GetVariable(int index) { if (_variables == null || _variables.Length == 0 || index >= _variables.Length) return null; return _variables[index]; diff --git a/source/Handlebars/ValueProviders/DataValues.cs b/source/Handlebars/ValueProviders/DataValues.cs index 6402c222..ea09ca24 100644 --- a/source/Handlebars/ValueProviders/DataValues.cs +++ b/source/Handlebars/ValueProviders/DataValues.cs @@ -7,7 +7,7 @@ namespace HandlebarsDotNet.ValueProviders public readonly ref struct DataValues { private readonly EntryIndex[] _wellKnownVariables; - private readonly FixedSizeDictionary _data; + private readonly FixedSizeDictionary _data; [MethodImpl(MethodImplOptions.AggressiveInlining)] public DataValues(BindingContext context) @@ -16,9 +16,9 @@ public DataValues(BindingContext context) _wellKnownVariables = context.WellKnownVariables; } - public T Value(ChainSegment segment) => (T) this[segment]; + public T? Value(ChainSegment segment) => (T?) this[segment]; - public object this[ChainSegment segment] + public object? this[ChainSegment segment] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get @@ -46,7 +46,7 @@ public object this[ChainSegment segment] } } - public object this[in EntryIndex entryIndex] + public object? this[in EntryIndex entryIndex] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _data[entryIndex]; @@ -68,7 +68,7 @@ public void CreateProperty(ChainSegment variable, out EntryIndex i } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CreateProperty(ChainSegment variable, object defaultValue, out EntryIndex index) + public void CreateProperty(ChainSegment variable, object? defaultValue, out EntryIndex index) { _data.AddOrReplace(variable, defaultValue, out index); diff --git a/source/Handlebars/ValueProviders/IteratorValues.cs b/source/Handlebars/ValueProviders/IteratorValues.cs index a12f774c..b0441554 100644 --- a/source/Handlebars/ValueProviders/IteratorValues.cs +++ b/source/Handlebars/ValueProviders/IteratorValues.cs @@ -7,7 +7,7 @@ namespace HandlebarsDotNet.ValueProviders { public readonly ref struct IteratorValues { - private readonly FixedSizeDictionary _data; + private readonly FixedSizeDictionary _data; private readonly EntryIndex[] _wellKnownVariables; @@ -24,7 +24,7 @@ public IteratorValues(BindingContext bindingContext) : this() _data.AddOrReplace(ChainSegment.Key, null, out _wellKnownVariables[(int)ChainSegment.Key.WellKnownVariable]); } - public object Value + public object? Value { get => _data[_wellKnownVariables[(int) ChainSegment.Value.WellKnownVariable]]; set => _data[_wellKnownVariables[(int) ChainSegment.Value.WellKnownVariable]] = value; @@ -32,17 +32,17 @@ public object Value public object First { - get => _data[_wellKnownVariables[(int) ChainSegment.First.WellKnownVariable]]; + get => _data[_wellKnownVariables[(int) ChainSegment.First.WellKnownVariable]]!; set => _data[_wellKnownVariables[(int) ChainSegment.First.WellKnownVariable]] = value; } public object Index { - get => _data[_wellKnownVariables[(int) ChainSegment.Index.WellKnownVariable]]; + get => _data[_wellKnownVariables[(int) ChainSegment.Index.WellKnownVariable]]!; set => _data[_wellKnownVariables[(int) ChainSegment.Index.WellKnownVariable]] = value; } - public object Key + public object? Key { get => _data[_wellKnownVariables[(int)ChainSegment.Key.WellKnownVariable]]; set => _data[_wellKnownVariables[(int)ChainSegment.Key.WellKnownVariable]] = value; @@ -50,7 +50,7 @@ public object Key public object Last { - get => _data[_wellKnownVariables[(int) ChainSegment.Last.WellKnownVariable]]; + get => _data[_wellKnownVariables[(int) ChainSegment.Last.WellKnownVariable]]!; set => _data[_wellKnownVariables[(int) ChainSegment.Last.WellKnownVariable]] = value; } } diff --git a/source/Handlebars/ValueProviders/ObjectIteratorValues.cs b/source/Handlebars/ValueProviders/ObjectIteratorValues.cs index cc6aade4..06252a55 100644 --- a/source/Handlebars/ValueProviders/ObjectIteratorValues.cs +++ b/source/Handlebars/ValueProviders/ObjectIteratorValues.cs @@ -2,13 +2,14 @@ using HandlebarsDotNet.PathStructure; using HandlebarsDotNet.Runtime; using System; +using System.Diagnostics.CodeAnalysis; namespace HandlebarsDotNet.ValueProviders { [Obsolete("Use IteratorValues")] public readonly ref struct ObjectIteratorValues { - private readonly FixedSizeDictionary _data; + private readonly FixedSizeDictionary _data; private readonly EntryIndex[] _wellKnownVariables; @@ -24,13 +25,13 @@ public ObjectIteratorValues(BindingContext bindingContext) : this() _data.AddOrReplace(ChainSegment.Index, BoxedValues.Zero, out _wellKnownVariables[(int) ChainSegment.Index.WellKnownVariable]); } - public object Key + public object? Key { get => _data[_wellKnownVariables[(int) ChainSegment.Key.WellKnownVariable]]; set => _data[_wellKnownVariables[(int) ChainSegment.Key.WellKnownVariable]] = value; } - public object Value + public object? Value { get => _data[_wellKnownVariables[(int) ChainSegment.Value.WellKnownVariable]]; set => _data[_wellKnownVariables[(int) ChainSegment.Value.WellKnownVariable]] = value; @@ -38,19 +39,19 @@ public object Value public object First { - get => _data[_wellKnownVariables[(int) ChainSegment.First.WellKnownVariable]]; + get => _data[_wellKnownVariables[(int) ChainSegment.First.WellKnownVariable]]!; set => _data[_wellKnownVariables[(int) ChainSegment.First.WellKnownVariable]] = value; } public object Index { - get => _data[_wellKnownVariables[(int) ChainSegment.Index.WellKnownVariable]]; + get => _data[_wellKnownVariables[(int) ChainSegment.Index.WellKnownVariable]]!; set => _data[_wellKnownVariables[(int) ChainSegment.Index.WellKnownVariable]] = value; } public object Last { - get => _data[_wellKnownVariables[(int) ChainSegment.Last.WellKnownVariable]]; + get => _data[_wellKnownVariables[(int) ChainSegment.Last.WellKnownVariable]]!; set => _data[_wellKnownVariables[(int) ChainSegment.Last.WellKnownVariable]] = value; } } diff --git a/source/Handlebars/_Delegates.cs b/source/Handlebars/_Delegates.cs index 350ac210..f059f264 100644 --- a/source/Handlebars/_Delegates.cs +++ b/source/Handlebars/_Delegates.cs @@ -24,7 +24,7 @@ namespace HandlebarsDotNet /// /// /// - public delegate object HandlebarsReturnHelper(Context context, Arguments arguments); + public delegate object? HandlebarsReturnHelper(Context context, Arguments arguments); /// /// InlineHelper: {{#helper arg1 arg2}}, supports value return @@ -32,7 +32,7 @@ namespace HandlebarsDotNet /// /// /// - public delegate object HandlebarsReturnWithOptionsHelper(in HelperOptions options, in Context context, in Arguments arguments); + public delegate object? HandlebarsReturnWithOptionsHelper(in HelperOptions options, in Context context, in Arguments arguments); /// /// BlockHelper: {{#helper}}..{{/helper}} @@ -49,7 +49,7 @@ namespace HandlebarsDotNet /// /// /// - public delegate object HandlebarsReturnBlockHelper(BlockHelperOptions options, Context context, Arguments arguments); + public delegate object? HandlebarsReturnBlockHelper(BlockHelperOptions options, Context context, Arguments arguments); public delegate TemplateDelegate HandlebarsBlockDecorator(TemplateDelegate function, in BlockDecoratorOptions options, in Context context, in Arguments arguments);