├── Source ├── RuntimeNullables │ ├── key.snk │ ├── stylecop.json │ ├── NullChecksAttribute.cs │ └── RuntimeNullables.csproj ├── TestAssemblyNonPublic │ ├── IPublicInterface.cs │ ├── IInternalInterface.cs │ ├── AssemblyAttributes.cs │ ├── FodyWeavers.xml │ ├── PublicContainingNestedNonPublic.cs │ ├── stylecop.json │ ├── InternalSubClass.cs │ ├── PublicBaseClass.cs │ ├── TestAssemblyNonPublic.csproj │ └── FodyWeavers.xsd ├── TestAssembly │ ├── NullableDisabled.cs │ ├── Record.cs │ ├── IsExternalInit.cs │ ├── FodyWeavers.xml │ ├── Combo.cs │ ├── stylecop.json │ ├── GeneratedCode.cs │ ├── Properties.cs │ ├── NestedContainer.cs │ ├── TestAssembly.csproj │ ├── Returns.cs │ ├── InputParameters.cs │ ├── Enumerables.cs │ ├── Enumerators.cs │ ├── OutputParameters.cs │ ├── AsyncEnumerables.cs │ ├── AsyncEnumerators.cs │ ├── FodyWeavers.xsd │ └── TaskResults.cs ├── RuntimeNullables.Fody │ ├── NullableValue.cs │ ├── Contexts │ │ ├── AssemblyContext.cs │ │ ├── GenericParameterInfo.cs │ │ ├── PropertyContext.cs │ │ ├── ModuleContext.cs │ │ ├── WeavingContext.cs │ │ ├── NullableContext.cs │ │ ├── MethodContext.cs │ │ └── TypeContext.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Extensions │ │ ├── InstructionExtensions.cs │ │ ├── InstructionCollectionExtensions.cs │ │ ├── ParameterDefinitionExtensions.cs │ │ ├── ReadOnlyListExtensions.cs │ │ ├── NullableValueExtensions.cs │ │ ├── MethodDefinitionExtensions.cs │ │ ├── TypeReferenceExtensions.cs │ │ └── AttributeProviderExtensions.cs │ ├── RuntimeNullablesException.cs │ ├── stylecop.json │ ├── RuntimeNullables.Fody.csproj │ ├── RuntimeNullables.Fody.xcf │ ├── ModuleReferences.cs │ ├── ReturnBlockInfo.cs │ ├── ILHelpers.cs │ ├── ModuleWeaver.cs │ └── BclReferences.cs ├── TestAssemblyPointers │ ├── Pointers.cs │ ├── FodyWeavers.xml │ ├── stylecop.json │ ├── TestAssemblyPointers.csproj │ └── FodyWeavers.xsd ├── TestAssemblyThrowHelpers │ ├── FodyWeavers.xml │ ├── stylecop.json │ ├── UsesThrowHelpers.cs │ ├── ThrowHelpers.cs │ ├── TestAssemblyThrowHelpers.csproj │ └── FodyWeavers.xsd ├── RuntimeNullables.Fody.Tests │ ├── NullableDisabledTests.cs │ ├── VerifyILTests.NullableDisabled.Debug.DotNet6_0.verified.txt │ ├── VerifyILTests.NullableDisabled.Debug.DotNet7_0.verified.txt │ ├── VerifyILTests.Pointers.Release.DotNet6_0.verified.txt │ ├── VerifyILTests.Pointers.Release.DotNet7_0.verified.txt │ ├── VerifyILTests.NullableDisabled.Release.DotNet6_0.verified.txt │ ├── VerifyILTests.NullableDisabled.Release.DotNet7_0.verified.txt │ ├── GeneratedCodeTests.cs │ ├── CheckOutputsFalseTests.cs │ ├── VerifyILTests.Pointers.Debug.DotNet6_0.verified.txt │ ├── VerifyILTests.Pointers.Debug.DotNet7_0.verified.txt │ ├── stylecop.json │ ├── PointerTests.cs │ ├── VerifyILTests.PublicContainingNestedNonPublic.Release.DotNet6_0.verified.txt │ ├── VerifyILTests.PublicContainingNestedNonPublic.Release.DotNet7_0.verified.txt │ ├── RecordTests.cs │ ├── VerifyILTests.PublicContainingNestedNonPublic.Debug.DotNet6_0.verified.txt │ ├── VerifyILTests.PublicContainingNestedNonPublic.Debug.DotNet7_0.verified.txt │ ├── NestedTests.cs │ ├── ThrowHelpersTests.cs │ ├── VersioningTests.cs │ ├── EnumerableTests.cs │ ├── VerifyILTests.InjectedThrowHelpers.Debug.DotNet6_0.verified.txt │ ├── VerifyILTests.InjectedThrowHelpers.Debug.DotNet7_0.verified.txt │ ├── VerifyILTests.InjectedThrowHelpers.Release.DotNet6_0.verified.txt │ ├── VerifyILTests.InjectedThrowHelpers.Release.DotNet7_0.verified.txt │ ├── AssemblyTests.cs │ ├── EnumeratorTests.cs │ ├── PropertyTests.cs │ ├── RuntimeNullables.Fody.Tests.csproj │ ├── VerifyILTests.GeneratedCode.Release.DotNet6_0.verified.txt │ ├── VerifyILTests.GeneratedCode.Release.DotNet7_0.verified.txt │ ├── VerifyILTests.GeneratedCode.Debug.DotNet6_0.verified.txt │ ├── VerifyILTests.GeneratedCode.Debug.DotNet7_0.verified.txt │ ├── VerifyILTests.PublicBaseClass.Release.DotNet6_0.verified.txt │ ├── VerifyILTests.PublicBaseClass.Release.DotNet7_0.verified.txt │ ├── OutputParameterTests.cs │ ├── ReturnTests.cs │ ├── NonPublicTests.cs │ ├── VerifyILTests.NestedContainer.Release.DotNet6_0.verified.txt │ ├── VerifyILTests.NestedContainer.Release.DotNet7_0.verified.txt │ ├── VerifyILTests.PublicBaseClass.Debug.DotNet6_0.verified.txt │ ├── VerifyILTests.PublicBaseClass.Debug.DotNet7_0.verified.txt │ ├── AsyncEnumerableTests.cs │ ├── VerifyPETests.cs │ ├── ComboAndMessageTests.cs │ ├── VerifyILTests.NestedContainer.Debug.DotNet6_0.verified.txt │ ├── VerifyILTests.NestedContainer.Debug.DotNet7_0.verified.txt │ ├── AsyncEnumeratorTests.cs │ ├── VerifyILTests.Combo.Release.DotNet6_0.verified.txt │ ├── VerifyILTests.Combo.Release.DotNet7_0.verified.txt │ ├── InputParametersTests.cs │ ├── VerifyILTests.Combo.Debug.DotNet6_0.verified.txt │ ├── VerifyILTests.Combo.Debug.DotNet7_0.verified.txt │ ├── VerifyILTests.InternalSubClass.Release.DotNet6_0.verified.txt │ ├── VerifyILTests.InternalSubClass.Release.DotNet7_0.verified.txt │ ├── VerifyILTests.InternalSubClass.Debug.DotNet6_0.verified.txt │ ├── VerifyILTests.InternalSubClass.Debug.DotNet7_0.verified.txt │ ├── TaskResultTests.cs │ ├── VerifyILTests.InputParameters.Release.DotNet6_0.verified.txt │ ├── VerifyILTests.InputParameters.Release.DotNet7_0.verified.txt │ ├── VerifyILTests.InputParameters.Debug.DotNet6_0.verified.txt │ ├── VerifyILTests.InputParameters.Debug.DotNet7_0.verified.txt │ ├── VerifyILTests.cs │ ├── VerifyILTests.Returns.Debug.DotNet6_0.verified.txt │ ├── VerifyILTests.Returns.Debug.DotNet7_0.verified.txt │ ├── VerifyILTests.Returns.Release.DotNet6_0.verified.txt │ └── VerifyILTests.Returns.Release.DotNet7_0.verified.txt ├── stylecop.json └── Directory.Build.props ├── Resources └── Singulink Icon 128x128.png ├── LICENSE └── .github └── workflows └── build-and-test.yml /Source/RuntimeNullables/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Singulink/RuntimeNullables/HEAD/Source/RuntimeNullables/key.snk -------------------------------------------------------------------------------- /Resources/Singulink Icon 128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Singulink/RuntimeNullables/HEAD/Resources/Singulink Icon 128x128.png -------------------------------------------------------------------------------- /Source/TestAssemblyNonPublic/IPublicInterface.cs: -------------------------------------------------------------------------------- 1 | namespace TestAssemblyNonPublic; 2 | 3 | public interface IPublicInterface 4 | { 5 | void InterfacePublicMethod(string value); 6 | } -------------------------------------------------------------------------------- /Source/TestAssemblyNonPublic/IInternalInterface.cs: -------------------------------------------------------------------------------- 1 | namespace TestAssemblyNonPublic; 2 | 3 | internal interface IInternalInterface 4 | { 5 | void InterfaceInternalMethod(string value); 6 | } -------------------------------------------------------------------------------- /Source/TestAssembly/NullableDisabled.cs: -------------------------------------------------------------------------------- 1 | namespace TestAssembly; 2 | 3 | #nullable disable 4 | 5 | public static class NullableDisabled 6 | { 7 | public static string NonNull(string x) => x; 8 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/NullableValue.cs: -------------------------------------------------------------------------------- 1 | namespace RuntimeNullables.Fody; 2 | 3 | internal enum NullableValue : byte 4 | { 5 | Oblivious = 0, 6 | NotAnnotated = 1, 7 | Annotated = 2, 8 | } -------------------------------------------------------------------------------- /Source/TestAssembly/Record.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TestAssembly; 6 | 7 | public record Record(string Name, int Value) 8 | { 9 | } -------------------------------------------------------------------------------- /Source/TestAssemblyNonPublic/AssemblyAttributes.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using RuntimeNullables; 3 | 4 | [assembly: NullChecks(false)] 5 | [assembly: InternalsVisibleTo("RuntimeNullables.Fody.Tests")] -------------------------------------------------------------------------------- /Source/TestAssemblyPointers/Pointers.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CA1801 // Review unused parameters 2 | namespace TestAssemblyPointers; 3 | 4 | public static class Pointers 5 | { 6 | public static unsafe int* Ignored(int* value) { return value; } 7 | } -------------------------------------------------------------------------------- /Source/TestAssembly/IsExternalInit.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | #pragma warning disable CA1812 4 | 5 | namespace System.Runtime.CompilerServices; 6 | 7 | [EditorBrowsable(EditorBrowsableState.Never)] 8 | internal static class IsExternalInit { } -------------------------------------------------------------------------------- /Source/TestAssemblyThrowHelpers/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Source/TestAssemblyNonPublic/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Source/TestAssembly/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Source/TestAssemblyPointers/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Contexts/AssemblyContext.cs: -------------------------------------------------------------------------------- 1 | using Mono.Cecil; 2 | 3 | namespace RuntimeNullables.Fody.Contexts; 4 | 5 | internal class AssemblyContext : NullableContext 6 | { 7 | public AssemblyContext(AssemblyDefinition assembly, WeavingContext weavingContext) : base(assembly, weavingContext) { } 8 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/NullableDisabledTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using TestAssembly; 3 | 4 | namespace RuntimeNullables.Fody.Tests; 5 | 6 | [TestClass] 7 | public class NullableDisabledTests 8 | { 9 | [TestMethod] 10 | public void NonNull() => NullableDisabled.NonNull(null); 11 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.NullableDisabled.Debug.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.NullableDisabled 2 | extends [System.Runtime]System.Object 3 | { 4 | .method public hidebysig static string 5 | NonNull(string x) cil managed 6 | { 7 | .maxstack 8 8 | IL_0000: ldarg.0 9 | IL_0001: ret 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.NullableDisabled.Debug.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.NullableDisabled 2 | extends [System.Runtime]System.Object 3 | { 4 | .method public hidebysig static string 5 | NonNull(string x) cil managed 6 | { 7 | .maxstack 8 8 | IL_0000: ldarg.0 9 | IL_0001: ret 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.Pointers.Release.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssemblyPointers.Pointers 2 | extends [System.Runtime]System.Object 3 | { 4 | .method public hidebysig static int32* 5 | Ignored(int32* 'value') cil managed 6 | { 7 | .maxstack 8 8 | IL_0000: ldarg.0 9 | IL_0001: ret 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.Pointers.Release.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssemblyPointers.Pointers 2 | extends [System.Runtime]System.Object 3 | { 4 | .method public hidebysig static int32* 5 | Ignored(int32* 'value') cil managed 6 | { 7 | .maxstack 8 8 | IL_0000: ldarg.0 9 | IL_0001: ret 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.NullableDisabled.Release.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.NullableDisabled 2 | extends [System.Runtime]System.Object 3 | { 4 | .method public hidebysig static string 5 | NonNull(string x) cil managed 6 | { 7 | .maxstack 8 8 | IL_0000: ldarg.0 9 | IL_0001: ret 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.NullableDisabled.Release.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.NullableDisabled 2 | extends [System.Runtime]System.Object 3 | { 4 | .method public hidebysig static string 5 | NonNull(string x) cil managed 6 | { 7 | .maxstack 8 8 | IL_0000: ldarg.0 9 | IL_0001: ret 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/GeneratedCodeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestAssembly; 4 | 5 | namespace RuntimeNullables.Fody.Tests; 6 | 7 | [TestClass] 8 | public class GeneratedCodeTests 9 | { 10 | [TestMethod] 11 | public void NoChecksOnGeneratedCode() 12 | { 13 | GeneratedCode.Method(null!); 14 | } 15 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "RuntimeNullables.Fody": { 4 | "commandName": "Executable", 5 | "executablePath": "C:\\Program Files\\dotnet\\dotnet.exe", 6 | "commandLineArgs": "msbuild -t:Rebuild", 7 | "workingDirectory": "C:\\Users\\mikem\\Documents\\Projects\\Singulink\\RuntimeNullables\\Source\\" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/CheckOutputsFalseTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using TestAssemblyNonPublic; 3 | 4 | namespace RuntimeNullables.Fody.Tests; 5 | 6 | [TestClass] 7 | public class CheckOutputsFalseTests 8 | { 9 | [TestMethod] 10 | public void CheckOutputsFalse() 11 | { 12 | PublicBaseClass.UncheckedBadReferenceReturn(); 13 | } 14 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Contexts/GenericParameterInfo.cs: -------------------------------------------------------------------------------- 1 | using Mono.Cecil; 2 | 3 | namespace RuntimeNullables.Fody.Contexts; 4 | 5 | internal struct GenericParameterInfo 6 | { 7 | public GenericParameter Parameter { get; } 8 | 9 | public bool Nullable { get; } 10 | 11 | public GenericParameterInfo(GenericParameter parameter, bool nullable) 12 | { 13 | Parameter = parameter; 14 | Nullable = nullable; 15 | } 16 | } -------------------------------------------------------------------------------- /Source/TestAssemblyNonPublic/PublicContainingNestedNonPublic.cs: -------------------------------------------------------------------------------- 1 | using RuntimeNullables; 2 | 3 | #pragma warning disable RCS1163 // Unused parameter. 4 | #pragma warning disable CA1801 // Review unused parameters 5 | 6 | namespace TestAssemblyNonPublic; 7 | 8 | [NullChecks(true)] 9 | public static class PublicContainingNestedNonPublic 10 | { 11 | internal static class Nested 12 | { 13 | public static void Method(string value) { } 14 | } 15 | } -------------------------------------------------------------------------------- /Source/TestAssembly/Combo.cs: -------------------------------------------------------------------------------- 1 | namespace TestAssembly; 2 | 3 | public class Combo where TClass : notnull 4 | { 5 | public string GetValue(bool returnNull, TClass inputValue, ref TMethod refValue, bool outputNull) where TMethod : class 6 | { 7 | string returnValue = returnNull ? null! : inputValue.ToString() + refValue.ToString(); 8 | 9 | if (outputNull) 10 | refValue = default!; 11 | 12 | return returnValue; 13 | } 14 | } -------------------------------------------------------------------------------- /Source/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // Enabling configuration: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 3 | 4 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 5 | "settings": { 6 | "documentationRules": { 7 | "documentInternalElements": false, 8 | "documentInterfaces": false 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Extensions/InstructionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Mono.Cecil.Cil; 2 | 3 | namespace RuntimeNullables.Fody.Extensions; 4 | 5 | public static class InstructionExtensions 6 | { 7 | public static Instruction Clone(this Instruction instruction) 8 | { 9 | if (instruction.Operand == null) 10 | return Instruction.Create(instruction.OpCode); 11 | 12 | return Instruction.Create(instruction.OpCode, (dynamic)instruction.Operand); 13 | } 14 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/RuntimeNullablesException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RuntimeNullables.Fody; 4 | 5 | public class RuntimeNullablesException : Exception 6 | { 7 | public RuntimeNullablesException() 8 | { 9 | } 10 | 11 | public RuntimeNullablesException(string message) : base(message) 12 | { 13 | } 14 | 15 | public RuntimeNullablesException(string message, Exception innerException) : base(message, innerException) 16 | { 17 | } 18 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // Enabling configuration: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 3 | 4 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 5 | "settings": { 6 | "documentationRules": { 7 | "documentExposedElements": true, 8 | "documentInternalElements": false 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/TestAssembly/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // Enabling configuration: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 3 | 4 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 5 | "settings": { 6 | "documentationRules": { 7 | "documentExposedElements": false, 8 | "documentInternalElements": false 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // Enabling configuration: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 3 | 4 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 5 | "settings": { 6 | "documentationRules": { 7 | "documentExposedElements": false, 8 | "documentInternalElements": false 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/TestAssemblyPointers/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // Enabling configuration: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 3 | 4 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 5 | "settings": { 6 | "documentationRules": { 7 | "documentExposedElements": false, 8 | "documentInternalElements": false 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.Pointers.Debug.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssemblyPointers.Pointers 2 | extends [System.Runtime]System.Object 3 | { 4 | .method public hidebysig static int32* 5 | Ignored(int32* 'value') cil managed 6 | { 7 | .maxstack 1 8 | .locals init (int32* V_0) 9 | IL_0000: nop 10 | IL_0001: ldarg.0 11 | IL_0002: stloc.0 12 | IL_0003: br.s IL_0005 13 | IL_0005: ldloc.0 14 | IL_0006: ret 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.Pointers.Debug.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssemblyPointers.Pointers 2 | extends [System.Runtime]System.Object 3 | { 4 | .method public hidebysig static int32* 5 | Ignored(int32* 'value') cil managed 6 | { 7 | .maxstack 1 8 | .locals init (int32* V_0) 9 | IL_0000: nop 10 | IL_0001: ldarg.0 11 | IL_0002: stloc.0 12 | IL_0003: br.s IL_0005 13 | IL_0005: ldloc.0 14 | IL_0006: ret 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/TestAssemblyThrowHelpers/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // Enabling configuration: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 3 | 4 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 5 | "settings": { 6 | "documentationRules": { 7 | "documentExposedElements": false, 8 | "documentInternalElements": false 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // Enabling configuration: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 3 | 4 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 5 | "settings": { 6 | "documentationRules": { 7 | "documentExposedElements": false, 8 | "documentInternalElements": false 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/PointerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using TestAssemblyPointers; 3 | 4 | namespace RuntimeNullables.Fody.Tests; 5 | 6 | [TestClass] 7 | public class PointerTests 8 | { 9 | private readonly int _value = 5; 10 | 11 | [TestMethod] 12 | public unsafe void PointerIgnored() 13 | { 14 | fixed (int* value = &_value) { 15 | Pointers.Ignored(value); 16 | Pointers.Ignored(null); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Source/TestAssembly/GeneratedCode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TestAssembly; 6 | 7 | [System.CodeDom.Compiler.GeneratedCodeAttribute("RuntimeNullables.Fody", "1.0.0")] 8 | public static class GeneratedCode 9 | { 10 | // Nested type without GeneratedCodeAttribute to ensure it doesn't trigger parent type context building (fixed in v1.0.5) 11 | public delegate void SomeDelegate(object obj); 12 | 13 | public static void Method(object obj) 14 | { 15 | } 16 | } -------------------------------------------------------------------------------- /Source/TestAssemblyNonPublic/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // Enabling configuration: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 3 | 4 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 5 | "settings": { 6 | "documentationRules": { 7 | "documentExposedElements": false, 8 | "documentInternalElements": false, 9 | "documentInterfaces": false 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Extensions/InstructionCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Mono.Cecil.Cil; 2 | using Mono.Collections.Generic; 3 | 4 | namespace RuntimeNullables.Fody.Extensions; 5 | 6 | internal static class InstructionCollectionExtensions 7 | { 8 | public static int LastIndexOf(this Collection collection, Instruction item) 9 | { 10 | for (int i = collection.Count - 1; i >= 0; i--) { 11 | if (collection[i] == item) 12 | return i; 13 | } 14 | 15 | return -1; 16 | } 17 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/RuntimeNullables.Fody.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 1591 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Source/TestAssemblyThrowHelpers/UsesThrowHelpers.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.Threading.Tasks; 3 | 4 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 5 | 6 | namespace TestAssemblyThrowHelpers; 7 | 8 | public static class UsesThrowHelpers 9 | { 10 | public static string ReturnParameter(string value) => value; 11 | 12 | public static string ReturnNullableParameterNonNullReturn(string? value) => value!; 13 | 14 | public static async Task ReturnAllowNullParameterNonNullReturnAsync([AllowNull] string value) => value!; 15 | } -------------------------------------------------------------------------------- /Source/TestAssemblyThrowHelpers/ThrowHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.IO; 4 | 5 | namespace RuntimeNullables; 6 | 7 | internal static class ThrowHelpers 8 | { 9 | // Pick different random exceptions just so we can make sure these helpers are being invoked. 10 | 11 | public static void ThrowArgumentNull(string paramName) => throw new InvalidOperationException(paramName); 12 | 13 | public static void ThrowOutputNull(string message) => throw new InvalidDataException(message); 14 | 15 | public static Exception GetAsyncResultNullException(string message) => new InvalidConstraintException(message); 16 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.PublicContainingNestedNonPublic.Release.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssemblyNonPublic.PublicContainingNestedNonPublic 2 | extends [System.Runtime]System.Object 3 | { 4 | .class abstract auto ansi sealed nested assembly beforefieldinit Nested 5 | extends [System.Runtime]System.Object 6 | { 7 | .method public hidebysig static void 8 | Method(string 'value') cil managed 9 | { 10 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 11 | .maxstack 8 12 | IL_0000: ret 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.PublicContainingNestedNonPublic.Release.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssemblyNonPublic.PublicContainingNestedNonPublic 2 | extends [System.Runtime]System.Object 3 | { 4 | .class abstract auto ansi sealed nested assembly beforefieldinit Nested 5 | extends [System.Runtime]System.Object 6 | { 7 | .method public hidebysig static void 8 | Method(string 'value') cil managed 9 | { 10 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 11 | .maxstack 8 12 | IL_0000: ret 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/RecordTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestAssembly; 4 | 5 | namespace RuntimeNullables.Fody.Tests; 6 | 7 | [TestClass] 8 | public class RecordTests 9 | { 10 | [TestMethod] 11 | public void Good() 12 | { 13 | var record = new Record("test", 5); 14 | record = record with { Name = "test2" }; 15 | } 16 | 17 | [TestMethod] 18 | public void Bad() 19 | { 20 | var record = new Record("test", 5); 21 | Assert.ThrowsException(() => record = record with { Name = null! }); 22 | } 23 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Extensions/ParameterDefinitionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Mono.Cecil; 2 | 3 | namespace RuntimeNullables.Fody.Extensions; 4 | 5 | internal static class ParameterDefinitionExtensions 6 | { 7 | // Preconditions: 8 | 9 | public static bool HasAllowNullAttribute(this ParameterDefinition parameter) 10 | { 11 | return parameter.HasAttribute("System.Diagnostics.CodeAnalysis.AllowNullAttribute"); 12 | } 13 | 14 | public static bool HasDisallowNullAttribute(this ParameterDefinition parameter) 15 | { 16 | return parameter.HasAttribute("System.Diagnostics.CodeAnalysis.DisallowNullAttribute"); 17 | } 18 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.PublicContainingNestedNonPublic.Debug.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssemblyNonPublic.PublicContainingNestedNonPublic 2 | extends [System.Runtime]System.Object 3 | { 4 | .class abstract auto ansi sealed nested assembly beforefieldinit Nested 5 | extends [System.Runtime]System.Object 6 | { 7 | .method public hidebysig static void 8 | Method(string 'value') cil managed 9 | { 10 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 11 | .maxstack 8 12 | IL_0000: nop 13 | IL_0001: ret 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.PublicContainingNestedNonPublic.Debug.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssemblyNonPublic.PublicContainingNestedNonPublic 2 | extends [System.Runtime]System.Object 3 | { 4 | .class abstract auto ansi sealed nested assembly beforefieldinit Nested 5 | extends [System.Runtime]System.Object 6 | { 7 | .method public hidebysig static void 8 | Method(string 'value') cil managed 9 | { 10 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 11 | .maxstack 8 12 | IL_0000: nop 13 | IL_0001: ret 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Extensions/ReadOnlyListExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace RuntimeNullables.Fody.Extensions; 4 | 5 | internal static class ReadOnlyListExtensions 6 | { 7 | public static int IndexOf(this IReadOnlyList readOnlyList, T item) 8 | { 9 | var comparer = EqualityComparer.Default; 10 | 11 | if (readOnlyList is IList list) 12 | return list.IndexOf(item); 13 | 14 | for (int i = 0; i < readOnlyList.Count; i++) { 15 | if (comparer.Equals(readOnlyList[i], item)) 16 | return i; 17 | } 18 | 19 | return -1; 20 | } 21 | } -------------------------------------------------------------------------------- /Source/TestAssemblyNonPublic/InternalSubClass.cs: -------------------------------------------------------------------------------- 1 | using RuntimeNullables; 2 | 3 | namespace TestAssemblyNonPublic; 4 | 5 | [NullChecks(true)] 6 | internal class InternalSubClass : PublicBaseClass, IPublicInterface, IInternalInterface 7 | { 8 | public override void AbstractPublicMethod(string value) { } 9 | 10 | public override void VirtualPublicMethod(string value) { } 11 | 12 | internal override void AbstractInternalMethod(string value) { } 13 | 14 | internal override void VirtualInternalMethod(string value) { } 15 | 16 | void IPublicInterface.InterfacePublicMethod(string value) { } 17 | 18 | void IInternalInterface.InterfaceInternalMethod(string value) { } 19 | } -------------------------------------------------------------------------------- /Source/TestAssembly/Properties.cs: -------------------------------------------------------------------------------- 1 | namespace TestAssembly; 2 | 3 | /// 4 | /// Properties are all initially null, even for non-nullable properties. 5 | /// 6 | public class Properties 7 | { 8 | private string _reference = null!; 9 | private string? _nullableReference; 10 | 11 | public string ReferenceAuto { get; set; } = null!; 12 | 13 | public string Reference { 14 | get => _reference; 15 | set => _reference = value; 16 | } 17 | 18 | public string? NullableReferenceAuto { get; set; } 19 | 20 | public string? NullableReference { 21 | get => _nullableReference; 22 | set => _nullableReference = value; 23 | } 24 | } -------------------------------------------------------------------------------- /Source/TestAssembly/NestedContainer.cs: -------------------------------------------------------------------------------- 1 | using RuntimeNullables; 2 | 3 | #pragma warning disable CA1801 // Review unused parameters 4 | 5 | namespace TestAssembly; 6 | 7 | public static class NestedContainer 8 | { 9 | [NullChecks(false)] 10 | public static class Nested 11 | { 12 | public static void Unchecked(string value) { } 13 | 14 | [NullChecks(true)] 15 | public static void Checked(string value) { } 16 | 17 | public static class NestedDeeper 18 | { 19 | public static void Unchecked(string value) { } 20 | 21 | [NullChecks(true)] 22 | public static void Checked(string value) { } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Source/TestAssemblyNonPublic/PublicBaseClass.cs: -------------------------------------------------------------------------------- 1 | using RuntimeNullables; 2 | 3 | namespace TestAssemblyNonPublic; 4 | 5 | [NullChecks(true)] 6 | public abstract class PublicBaseClass 7 | { 8 | /// 9 | /// CheckOutputs is turned off for this project so this should work without throwing an exception. 10 | /// 11 | public static string UncheckedBadReferenceReturn() => null!; 12 | 13 | public abstract void AbstractPublicMethod(string value); 14 | 15 | public virtual void VirtualPublicMethod(string value) { } 16 | 17 | internal abstract void AbstractInternalMethod(string value); 18 | 19 | internal virtual void VirtualInternalMethod(string value) { } 20 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/RuntimeNullables.Fody.xcf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Determines whether outputs (return values and out/ref parameters) have null checks injected. 6 | 7 | 8 | 9 | 10 | Determines whether non-public members have null checks injected or if only public entry points are checked. 11 | 12 | 13 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Extensions/NullableValueExtensions.cs: -------------------------------------------------------------------------------- 1 | using RuntimeNullables.Fody.Contexts; 2 | 3 | namespace RuntimeNullables.Fody.Extensions; 4 | 5 | internal static class NullableValueExtensions 6 | { 7 | public static bool ToIsNullable(this NullableValue nullableValue, WeavingContext weavingContext) 8 | { 9 | switch (nullableValue) { 10 | case NullableValue.Oblivious: 11 | case NullableValue.Annotated: 12 | return true; 13 | case NullableValue.NotAnnotated: 14 | return false; 15 | default: 16 | weavingContext.WriteError("Unexpected nullable argument value."); 17 | return true; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/NestedTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestAssembly; 4 | using TestAssemblyNonPublic; 5 | 6 | namespace RuntimeNullables.Fody.Tests; 7 | 8 | [TestClass] 9 | public class NestedTests 10 | { 11 | [TestMethod] 12 | public void NoNullChecksNested() 13 | { 14 | NestedContainer.Nested.Unchecked(null!); 15 | NestedContainer.Nested.NestedDeeper.Unchecked(null!); 16 | } 17 | 18 | [TestMethod] 19 | public void NullChecksNested() 20 | { 21 | Assert.ThrowsException(() => NestedContainer.Nested.Checked(null!)); 22 | Assert.ThrowsException(() => NestedContainer.Nested.NestedDeeper.Checked(null!)); 23 | } 24 | 25 | [TestMethod] 26 | public void PublicContainingNestedNonPublicClass() 27 | { 28 | PublicContainingNestedNonPublic.Nested.Method(null!); 29 | } 30 | } -------------------------------------------------------------------------------- /Source/TestAssemblyPointers/TestAssemblyPointers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0 5 | true 6 | 7 | 8 | 1591 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | all 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/ThrowHelpersTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.IO; 4 | using System.Threading.Tasks; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using TestAssemblyThrowHelpers; 7 | 8 | namespace RuntimeNullables.Fody.Tests; 9 | 10 | [TestClass] 11 | public class ThrowHelpersTests 12 | { 13 | [TestMethod] 14 | public void ArgumentNull() 15 | { 16 | Assert.ThrowsException(() => UsesThrowHelpers.ReturnParameter(null!)); 17 | } 18 | 19 | [TestMethod] 20 | public void OutputNull() 21 | { 22 | Assert.ThrowsException(() => UsesThrowHelpers.ReturnNullableParameterNonNullReturn(null)); 23 | } 24 | 25 | [TestMethod] 26 | public Task OutputNullAsync() 27 | { 28 | return Assert.ThrowsExceptionAsync(() => UsesThrowHelpers.ReturnAllowNullParameterNonNullReturnAsync(null)); 29 | } 30 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables/NullChecksAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RuntimeNullables; 4 | 5 | /// 6 | /// Indicates whether nullable reference type null checks should be injected. 7 | /// 8 | [AttributeUsage( 9 | AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | 10 | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.ReturnValue, 11 | Inherited = false)] 12 | public sealed class NullChecksAttribute : Attribute 13 | { 14 | /// 15 | /// Gets a value indicating whether nullable reference type null checks should be injected. 16 | /// 17 | public bool Enabled { get; } 18 | 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | public NullChecksAttribute(bool enabled) 23 | { 24 | Enabled = enabled; 25 | } 26 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VersioningTests.cs: -------------------------------------------------------------------------------- 1 | using System.CodeDom.Compiler; 2 | using System.Reflection; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using TestAssembly; 5 | 6 | namespace RuntimeNullables.Fody.Tests; 7 | 8 | [TestClass] 9 | public class VersioningTests 10 | { 11 | [TestMethod] 12 | public void GeneratedCodeToolVersion() 13 | { 14 | // Ensure that the GeneratedCodeAttribute version placed on the throw helper class is correct. 15 | 16 | var throwHelperType = typeof(InputParameters).Assembly.GetType("RuntimeNullables.ThrowHelpers"); 17 | string generatedCodeVersion = throwHelperType!.GetCustomAttribute()!.Version; 18 | 19 | string weaverVersion = new AssemblyName(typeof(ModuleWeaver).Assembly.FullName!).Version!.ToString(); 20 | string addinVersion = new AssemblyName(typeof(NullChecksAttribute).Assembly.FullName!).Version!.ToString(); 21 | 22 | Assert.AreEqual(weaverVersion, generatedCodeVersion); 23 | Assert.AreEqual(addinVersion, generatedCodeVersion); 24 | } 25 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/ModuleReferences.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mono.Cecil; 3 | 4 | namespace RuntimeNullables.Fody; 5 | 6 | internal class ModuleReferences 7 | { 8 | private readonly MethodReference? _throwOutputNullMethod; 9 | private readonly MethodReference? _getAsyncResultNullExceptionMethod; 10 | 11 | public MethodReference ThrowArgumentNullMethod { get; } 12 | 13 | public MethodReference ThrowOutputNullMethod => _throwOutputNullMethod ?? throw new InvalidOperationException("Output throw helper not set."); 14 | 15 | public MethodReference GetAsyncResultNullExceptionMethod => _getAsyncResultNullExceptionMethod ?? throw new InvalidOperationException("Output throw helper not set."); 16 | 17 | public ModuleReferences(MethodReference throwArgumentNullMethod, MethodReference? throwOutputNullMethod, MethodReference? getAsyncResultNullExceptionMethod) 18 | { 19 | ThrowArgumentNullMethod = throwArgumentNullMethod; 20 | _throwOutputNullMethod = throwOutputNullMethod; 21 | _getAsyncResultNullExceptionMethod = getAsyncResultNullExceptionMethod; 22 | } 23 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Singulink 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Contexts/PropertyContext.cs: -------------------------------------------------------------------------------- 1 | using Mono.Cecil; 2 | 3 | namespace RuntimeNullables.Fody.Contexts; 4 | 5 | internal sealed class PropertyContext : NullableContext 6 | { 7 | public PropertyDefinition Property { get; } 8 | 9 | public TypeContext TypeContext { get; } 10 | 11 | public MethodContext? GetMethodContext { get; private set; } 12 | 13 | public MethodContext? SetMethodContext { get; private set; } 14 | 15 | public PropertyContext(PropertyDefinition property, TypeContext typeContext) : base(property, typeContext) 16 | { 17 | Property = property; 18 | TypeContext = typeContext; 19 | } 20 | 21 | public override void Build() 22 | { 23 | base.Build(); 24 | 25 | if (Property.GetMethod != null) { 26 | GetMethodContext = new MethodContext(Property.GetMethod, this); 27 | GetMethodContext.Build(); 28 | } 29 | 30 | if (Property.SetMethod != null) { 31 | SetMethodContext = new MethodContext(Property.SetMethod, this); 32 | SetMethodContext.Build(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Extensions/MethodDefinitionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Mono.Cecil; 2 | using RuntimeNullables.Fody.Contexts; 3 | 4 | namespace RuntimeNullables.Fody.Extensions; 5 | 6 | internal static class MethodDefinitionExtensions 7 | { 8 | public static TypeDefinition? GetIteratorStateMachineType(this MethodDefinition method, WeavingContext weavingContext) 9 | { 10 | return method.GetConstructorArgValue("System.Runtime.CompilerServices.IteratorStateMachineAttribute", weavingContext)?.Resolve(); 11 | } 12 | 13 | public static TypeDefinition? GetAsyncStateMachineType(this MethodDefinition method, WeavingContext weavingContext) 14 | { 15 | return method.GetConstructorArgValue("System.Runtime.CompilerServices.AsyncStateMachineAttribute", weavingContext)?.Resolve(); 16 | } 17 | 18 | public static TypeDefinition? GetAsyncIteratorStateMachineType(this MethodDefinition method, WeavingContext weavingContext) 19 | { 20 | return method.GetConstructorArgValue("System.Runtime.CompilerServices.AsyncIteratorStateMachineAttribute", weavingContext)?.Resolve(); 21 | } 22 | } -------------------------------------------------------------------------------- /Source/TestAssemblyThrowHelpers/TestAssemblyThrowHelpers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0 5 | 6 | 1591 7 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | all 25 | runtime; build; native; contentfiles; analyzers; buildtransitive 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Source/TestAssembly/TestAssembly.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0 5 | 6 | 1591 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Source/TestAssembly/Returns.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using RuntimeNullables; 3 | 4 | namespace TestAssembly; 5 | 6 | public static class Returns 7 | { 8 | public static string GoodReference() => string.Empty; 9 | 10 | public static string BadReference() => null!; 11 | 12 | public static string? NullableReference() => null; 13 | 14 | [return: NullChecks(false)] 15 | public static string NoNullChecksBadReference() => null!; 16 | 17 | [NullChecks(false)] 18 | [return: NullChecks(true)] 19 | public static string NullChecksOnNoNullChecksMethodBadReference() => null!; 20 | 21 | public static T GoodGenericNotNullConstraint() where T : notnull, new() => new T(); 22 | 23 | public static T BadGenericNotNullConstraint() where T : notnull, new() => default!; 24 | 25 | [return: NotNull] 26 | public static T GoodGenericNotNullAttribute() where T : new() => new T(); 27 | 28 | [return: NotNull] 29 | public static T BadGenericNotNullAttribute() => default!; 30 | 31 | [return: MaybeNull] 32 | public static T GenericMaybeNullAttribute() where T : class => null; 33 | 34 | public static T GenericUnconstrained() => default!; 35 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/EnumerableTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestAssembly; 4 | 5 | namespace RuntimeNullables.Fody.Tests; 6 | 7 | [TestClass] 8 | public class EnumerableTests 9 | { 10 | [TestMethod] 11 | public void GoodGetReferences() 12 | { 13 | foreach (var value in Enumerables.GoodGetReferences()) { } 14 | } 15 | 16 | [TestMethod] 17 | public void BadGetReferences() 18 | { 19 | Assert.ThrowsException(() => { 20 | foreach (var value in Enumerables.BadGetReferences()) { } 21 | }); 22 | } 23 | 24 | [TestMethod] 25 | public void GoodGetGenerics() 26 | { 27 | foreach (var value in Enumerables.GoodGetGenerics()) { } 28 | } 29 | 30 | [TestMethod] 31 | public void BadGetGenerics() 32 | { 33 | Assert.ThrowsException(() => { 34 | foreach (var value in Enumerables.BadGetGenerics()) { } 35 | }); 36 | } 37 | 38 | [TestMethod] 39 | public void GetNullNonGenerics() 40 | { 41 | foreach (var value in Enumerables.GetNullNonGenerics()) { } 42 | } 43 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.InjectedThrowHelpers.Debug.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class private abstract auto ansi sealed beforefieldinit RuntimeNullables.ThrowHelpers 2 | extends [System.Runtime]System.Object 3 | { 4 | string) = ( 01 00 15 52 75 6E 74 69 6D 65 4E 75 6C 6C 61 62 // ...RuntimeNullab 5 | 6C 65 73 2E 46 6F 64 79 07 32 2E 30 2E 30 2E 30 // les.Fody.2.0.0.0 6 | 00 00 ) 7 | .method assembly hidebysig static void 8 | ThrowArgumentNull(string paramName) cil managed 9 | { 10 | .maxstack 8 11 | IL_0000: ldarg.0 12 | IL_0001: newobj instance void [System.Runtime]System.ArgumentNullException::.ctor(string) 13 | IL_0006: throw 14 | } 15 | .method assembly hidebysig static void 16 | ThrowOutputNull(string message) cil managed 17 | { 18 | .maxstack 8 19 | IL_0000: ldarg.0 20 | IL_0001: newobj instance void [System.Runtime]System.NullReferenceException::.ctor(string) 21 | IL_0006: throw 22 | } 23 | .method assembly hidebysig static class [System.Runtime]System.Exception 24 | GetAsyncResultNullException(string message) cil managed 25 | { 26 | .maxstack 8 27 | IL_0000: ldarg.0 28 | IL_0001: newobj instance void [System.Runtime]System.NullReferenceException::.ctor(string) 29 | IL_0006: ret 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.InjectedThrowHelpers.Debug.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class private abstract auto ansi sealed beforefieldinit RuntimeNullables.ThrowHelpers 2 | extends [System.Runtime]System.Object 3 | { 4 | string) = ( 01 00 15 52 75 6E 74 69 6D 65 4E 75 6C 6C 61 62 // ...RuntimeNullab 5 | 6C 65 73 2E 46 6F 64 79 07 32 2E 30 2E 30 2E 30 // les.Fody.2.0.0.0 6 | 00 00 ) 7 | .method assembly hidebysig static void 8 | ThrowArgumentNull(string paramName) cil managed 9 | { 10 | .maxstack 8 11 | IL_0000: ldarg.0 12 | IL_0001: newobj instance void [System.Runtime]System.ArgumentNullException::.ctor(string) 13 | IL_0006: throw 14 | } 15 | .method assembly hidebysig static void 16 | ThrowOutputNull(string message) cil managed 17 | { 18 | .maxstack 8 19 | IL_0000: ldarg.0 20 | IL_0001: newobj instance void [System.Runtime]System.NullReferenceException::.ctor(string) 21 | IL_0006: throw 22 | } 23 | .method assembly hidebysig static class [System.Runtime]System.Exception 24 | GetAsyncResultNullException(string message) cil managed 25 | { 26 | .maxstack 8 27 | IL_0000: ldarg.0 28 | IL_0001: newobj instance void [System.Runtime]System.NullReferenceException::.ctor(string) 29 | IL_0006: ret 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.InjectedThrowHelpers.Release.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class private abstract auto ansi sealed beforefieldinit RuntimeNullables.ThrowHelpers 2 | extends [System.Runtime]System.Object 3 | { 4 | string) = ( 01 00 15 52 75 6E 74 69 6D 65 4E 75 6C 6C 61 62 // ...RuntimeNullab 5 | 6C 65 73 2E 46 6F 64 79 07 32 2E 30 2E 30 2E 30 // les.Fody.2.0.0.0 6 | 00 00 ) 7 | .method assembly hidebysig static void 8 | ThrowArgumentNull(string paramName) cil managed 9 | { 10 | .maxstack 8 11 | IL_0000: ldarg.0 12 | IL_0001: newobj instance void [System.Runtime]System.ArgumentNullException::.ctor(string) 13 | IL_0006: throw 14 | } 15 | .method assembly hidebysig static void 16 | ThrowOutputNull(string message) cil managed 17 | { 18 | .maxstack 8 19 | IL_0000: ldarg.0 20 | IL_0001: newobj instance void [System.Runtime]System.NullReferenceException::.ctor(string) 21 | IL_0006: throw 22 | } 23 | .method assembly hidebysig static class [System.Runtime]System.Exception 24 | GetAsyncResultNullException(string message) cil managed 25 | { 26 | .maxstack 8 27 | IL_0000: ldarg.0 28 | IL_0001: newobj instance void [System.Runtime]System.NullReferenceException::.ctor(string) 29 | IL_0006: ret 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.InjectedThrowHelpers.Release.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class private abstract auto ansi sealed beforefieldinit RuntimeNullables.ThrowHelpers 2 | extends [System.Runtime]System.Object 3 | { 4 | string) = ( 01 00 15 52 75 6E 74 69 6D 65 4E 75 6C 6C 61 62 // ...RuntimeNullab 5 | 6C 65 73 2E 46 6F 64 79 07 32 2E 30 2E 30 2E 30 // les.Fody.2.0.0.0 6 | 00 00 ) 7 | .method assembly hidebysig static void 8 | ThrowArgumentNull(string paramName) cil managed 9 | { 10 | .maxstack 8 11 | IL_0000: ldarg.0 12 | IL_0001: newobj instance void [System.Runtime]System.ArgumentNullException::.ctor(string) 13 | IL_0006: throw 14 | } 15 | .method assembly hidebysig static void 16 | ThrowOutputNull(string message) cil managed 17 | { 18 | .maxstack 8 19 | IL_0000: ldarg.0 20 | IL_0001: newobj instance void [System.Runtime]System.NullReferenceException::.ctor(string) 21 | IL_0006: throw 22 | } 23 | .method assembly hidebysig static class [System.Runtime]System.Exception 24 | GetAsyncResultNullException(string message) cil managed 25 | { 26 | .maxstack 8 27 | IL_0000: ldarg.0 28 | IL_0001: newobj instance void [System.Runtime]System.NullReferenceException::.ctor(string) 29 | IL_0006: ret 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/TestAssembly/InputParameters.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using RuntimeNullables; 3 | 4 | #pragma warning disable CA1801 // Review unused parameters 5 | 6 | namespace TestAssembly; 7 | 8 | public static class InputParameters 9 | { 10 | public static void Reference(string value) { } 11 | 12 | [NullChecks(false)] 13 | public static void NoNullChecksReference(string value) { } 14 | 15 | public static void NullableReference(string? value) { } 16 | 17 | public static void ValueArray(byte[] value) { } 18 | 19 | public static void GenericUnconstrained(T value) { } 20 | 21 | public static void GenericUnconstrainedIn(in T value) { } 22 | 23 | public static void GenericNotNullConstraint(T value) where T : notnull { } 24 | 25 | public static void GenericDisallowNull([DisallowNull] T value) { } 26 | 27 | public static void GenericClassConstraint(T value) where T : class { } 28 | 29 | public static void GenericClassConstraintWithAllowNull([AllowNull] T value) where T : class { } 30 | 31 | public static void GenericClassConstraintWithNullable(T? value) where T : class { } 32 | 33 | public static void GenericNullableClassConstraint(T value) where T : class? { } 34 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/AssemblyTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection.Metadata; 4 | using System.Reflection.PortableExecutable; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | 7 | namespace RuntimeNullables.Fody.Tests; 8 | 9 | [TestClass] 10 | public class AssemblyTests 11 | { 12 | [TestMethod] 13 | public void NoValueTupleReferences() 14 | { 15 | // System.ValueTuple may cause issues in some configurations, avoid using it. 16 | 17 | using var fileStream = File.OpenRead(typeof(ModuleWeaver).Assembly.Location); 18 | using var peReader = new PEReader(fileStream); 19 | var metadataReader = peReader.GetMetadataReader(); 20 | 21 | foreach (var typeRefHandle in metadataReader.TypeReferences) { 22 | var typeRef = metadataReader.GetTypeReference(typeRefHandle); 23 | 24 | string typeNamespace = metadataReader.GetString(typeRef.Namespace); 25 | if (typeNamespace != typeof(ValueTuple).Namespace) 26 | continue; 27 | 28 | string typeName = metadataReader.GetString(typeRef.Name); 29 | Assert.IsFalse(typeName.Contains(nameof(ValueTuple), StringComparison.Ordinal)); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Source/TestAssembly/Enumerables.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | 4 | namespace TestAssembly; 5 | 6 | public static class Enumerables 7 | { 8 | public static IEnumerable GoodGetReferences() 9 | { 10 | yield return string.Empty; 11 | yield return string.Empty; 12 | yield return string.Empty; 13 | yield return string.Empty; 14 | yield return string.Empty; 15 | } 16 | 17 | public static IEnumerable BadGetReferences() 18 | { 19 | yield return string.Empty; 20 | yield return null!; 21 | yield return string.Empty; 22 | } 23 | 24 | public static IEnumerable GoodGetGenerics() where T : notnull, new() 25 | { 26 | yield return new T(); 27 | yield return new T(); 28 | yield return new T(); 29 | yield return new T(); 30 | yield return new T(); 31 | } 32 | 33 | public static IEnumerable BadGetGenerics() where T : notnull, new() 34 | { 35 | yield return new T(); 36 | yield return default!; 37 | yield return new T(); 38 | } 39 | 40 | public static IEnumerable GetNullNonGenerics() 41 | { 42 | yield return null; 43 | } 44 | } -------------------------------------------------------------------------------- /Source/TestAssembly/Enumerators.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | 4 | namespace TestAssembly; 5 | 6 | public static class Enumerators 7 | { 8 | public static IEnumerator GoodGetReferences() 9 | { 10 | yield return string.Empty; 11 | yield return string.Empty; 12 | yield return string.Empty; 13 | yield return string.Empty; 14 | yield return string.Empty; 15 | } 16 | 17 | public static IEnumerator BadGetReferences() 18 | { 19 | yield return string.Empty; 20 | yield return null!; 21 | yield return string.Empty; 22 | } 23 | 24 | public static IEnumerator GoodGetGenerics() where T : notnull, new() 25 | { 26 | yield return new T(); 27 | yield return new T(); 28 | yield return new T(); 29 | yield return new T(); 30 | yield return new T(); 31 | } 32 | 33 | public static IEnumerator BadGetGenerics() where T : notnull, new() 34 | { 35 | yield return new T(); 36 | yield return default!; 37 | yield return new T(); 38 | } 39 | 40 | public static IEnumerator GetNullNonGenerics() 41 | { 42 | yield return null; 43 | } 44 | } -------------------------------------------------------------------------------- /Source/TestAssemblyNonPublic/TestAssemblyNonPublic.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0 5 | 10 | 11 | 12 | 1591 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | all 27 | runtime; build; native; contentfiles; analyzers; buildtransitive 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Source/TestAssembly/OutputParameters.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace TestAssembly; 4 | 5 | public static class OutputParameters 6 | { 7 | public static void GoodReference(out string value) => value = string.Empty; 8 | 9 | public static void GoodReferenceWithTryFinally(out string value) 10 | { 11 | value = string.Empty; 12 | 13 | try { 14 | value += string.Empty; 15 | } 16 | finally { 17 | value += string.Empty; 18 | } 19 | } 20 | 21 | public static void BadReference(out string value) => value = null!; 22 | 23 | public static void NullableReference(out string? value) => value = null; 24 | 25 | public static void GoodGenericNotNullConstraint(out T value) where T : notnull, new() => value = new T(); 26 | 27 | public static void BadGenericNotNullConstraint(out T value) where T : notnull, new() => value = default!; 28 | 29 | public static void GoodGenericNotNullAttribute([NotNull] out T value) where T : new() => value = new T(); 30 | 31 | public static void BadGenericNotNullAttribute([NotNull] out T value) => value = default!; 32 | 33 | public static void GenericMaybeNullAttribute([MaybeNull] out T value) where T : class => value = null; 34 | 35 | public static void GenericUnconstrained(out T value) => value = default!; 36 | } -------------------------------------------------------------------------------- /Source/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12.0 4 | enable 5 | 6.0.0 6 | true 7 | 2.0.0 8 | $(DefineConstants);CI 9 | 10 | 11 | 12 | true 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/EnumeratorTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestAssembly; 4 | 5 | namespace RuntimeNullables.Fody.Tests; 6 | 7 | [TestClass] 8 | public class EnumeratorTests 9 | { 10 | [TestMethod] 11 | public void GoodGetReferences() 12 | { 13 | var enumerator = Enumerators.GoodGetReferences(); 14 | while (enumerator.MoveNext()) { } 15 | } 16 | 17 | [TestMethod] 18 | public void BadGetReferences() 19 | { 20 | Assert.ThrowsException(() => { 21 | var enumerator = Enumerators.BadGetReferences(); 22 | while (enumerator.MoveNext()) { } 23 | }); 24 | } 25 | 26 | [TestMethod] 27 | public void GoodGetGenerics() 28 | { 29 | var enumerator = Enumerators.GoodGetGenerics(); 30 | while (enumerator.MoveNext()) { } 31 | } 32 | 33 | [TestMethod] 34 | public void BadGetGenerics() 35 | { 36 | Assert.ThrowsException(() => { 37 | var enumerator = Enumerators.BadGetGenerics(); 38 | while (enumerator.MoveNext()) { } 39 | }); 40 | } 41 | 42 | [TestMethod] 43 | public void GetNullNonGenerics() 44 | { 45 | var enumerator = Enumerators.GetNullNonGenerics(); 46 | while (enumerator.MoveNext()) { } 47 | } 48 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/PropertyTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestAssembly; 4 | 5 | namespace RuntimeNullables.Fody.Tests; 6 | 7 | [TestClass] 8 | public class PropertyTests 9 | { 10 | [TestMethod] 11 | public void NullableReference() 12 | { 13 | var instance = new Properties(); 14 | 15 | instance.NullableReference = null; 16 | _ = instance.NullableReference; 17 | } 18 | 19 | [TestMethod] 20 | public void NullableReferenceAuto() 21 | { 22 | var instance = new Properties(); 23 | 24 | instance.NullableReferenceAuto = null; 25 | _ = instance.NullableReferenceAuto; 26 | } 27 | 28 | [TestMethod] 29 | public void Reference() 30 | { 31 | var instance = new Properties(); 32 | 33 | Assert.ThrowsException(() => instance.Reference = null!); 34 | Assert.ThrowsException(() => instance.Reference); 35 | 36 | instance.Reference = string.Empty; 37 | _ = instance.Reference; 38 | } 39 | 40 | [TestMethod] 41 | public void ReferenceAuto() 42 | { 43 | var instance = new Properties(); 44 | 45 | Assert.ThrowsException(() => instance.ReferenceAuto = null!); 46 | Assert.ThrowsException(() => instance.ReferenceAuto); 47 | 48 | instance.ReferenceAuto = string.Empty; 49 | _ = instance.ReferenceAuto; 50 | } 51 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/RuntimeNullables.Fody.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0 5 | 6 | 1591 7 | false 8 | true 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.GeneratedCode.Release.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.GeneratedCode 2 | extends [System.Runtime]System.Object 3 | { 4 | string) = ( 01 00 15 52 75 6E 74 69 6D 65 4E 75 6C 6C 61 62 // ...RuntimeNullab 5 | 6C 65 73 2E 46 6F 64 79 05 31 2E 30 2E 30 00 00 ) // les.Fody.1.0.0.. 6 | .class auto ansi sealed nested public SomeDelegate 7 | extends [System.Runtime]System.MulticastDelegate 8 | { 9 | .method public hidebysig specialname rtspecialname 10 | instance void .ctor(object 'object', 11 | native int 'method') runtime managed 12 | { 13 | } 14 | .method public hidebysig newslot virtual 15 | instance void Invoke(object obj) runtime managed 16 | { 17 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 18 | } 19 | .method public hidebysig newslot virtual 20 | instance class [System.Runtime]System.IAsyncResult 21 | BeginInvoke(object obj, 22 | class [System.Runtime]System.AsyncCallback callback, 23 | object 'object') runtime managed 24 | { 25 | .param [1] 26 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 27 | } 28 | .method public hidebysig newslot virtual 29 | instance void EndInvoke(class [System.Runtime]System.IAsyncResult result) runtime managed 30 | { 31 | } 32 | } 33 | .method public hidebysig static void Method(object obj) cil managed 34 | { 35 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 36 | .maxstack 8 37 | IL_0000: ret 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.GeneratedCode.Release.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.GeneratedCode 2 | extends [System.Runtime]System.Object 3 | { 4 | string) = ( 01 00 15 52 75 6E 74 69 6D 65 4E 75 6C 6C 61 62 // ...RuntimeNullab 5 | 6C 65 73 2E 46 6F 64 79 05 31 2E 30 2E 30 00 00 ) // les.Fody.1.0.0.. 6 | .class auto ansi sealed nested public SomeDelegate 7 | extends [System.Runtime]System.MulticastDelegate 8 | { 9 | .method public hidebysig specialname rtspecialname 10 | instance void .ctor(object 'object', 11 | native int 'method') runtime managed 12 | { 13 | } 14 | .method public hidebysig newslot virtual 15 | instance void Invoke(object obj) runtime managed 16 | { 17 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 18 | } 19 | .method public hidebysig newslot virtual 20 | instance class [System.Runtime]System.IAsyncResult 21 | BeginInvoke(object obj, 22 | class [System.Runtime]System.AsyncCallback callback, 23 | object 'object') runtime managed 24 | { 25 | .param [1] 26 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 27 | } 28 | .method public hidebysig newslot virtual 29 | instance void EndInvoke(class [System.Runtime]System.IAsyncResult result) runtime managed 30 | { 31 | } 32 | } 33 | .method public hidebysig static void Method(object obj) cil managed 34 | { 35 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 36 | .maxstack 8 37 | IL_0000: ret 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/RuntimeNullables/RuntimeNullables.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | NullGuard, Null Check, NRT, Nullable, ILWeaving, Fody, Cecil, AOP 5 | Automatic null check injection for runtime C# Nullable Reference Types (NRT) parameter/contract validation. 6 | MIT 7 | https://github.com/Singulink/RuntimeNullables 8 | Singulink 9 | false 10 | © Singulink. All rights reserved. 11 | true 12 | key.snk 13 | Singulink Icon 128x128.png 14 | README.md 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | True 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.GeneratedCode.Debug.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.GeneratedCode 2 | extends [System.Runtime]System.Object 3 | { 4 | string) = ( 01 00 15 52 75 6E 74 69 6D 65 4E 75 6C 6C 61 62 // ...RuntimeNullab 5 | 6C 65 73 2E 46 6F 64 79 05 31 2E 30 2E 30 00 00 ) // les.Fody.1.0.0.. 6 | .class auto ansi sealed nested public SomeDelegate 7 | extends [System.Runtime]System.MulticastDelegate 8 | { 9 | .method public hidebysig specialname rtspecialname 10 | instance void .ctor(object 'object', 11 | native int 'method') runtime managed 12 | { 13 | } 14 | .method public hidebysig newslot virtual 15 | instance void Invoke(object obj) runtime managed 16 | { 17 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 18 | } 19 | .method public hidebysig newslot virtual 20 | instance class [System.Runtime]System.IAsyncResult 21 | BeginInvoke(object obj, 22 | class [System.Runtime]System.AsyncCallback callback, 23 | object 'object') runtime managed 24 | { 25 | .param [1] 26 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 27 | } 28 | .method public hidebysig newslot virtual 29 | instance void EndInvoke(class [System.Runtime]System.IAsyncResult result) runtime managed 30 | { 31 | } 32 | } 33 | .method public hidebysig static void Method(object obj) cil managed 34 | { 35 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 36 | .maxstack 8 37 | IL_0000: nop 38 | IL_0001: ret 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.GeneratedCode.Debug.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.GeneratedCode 2 | extends [System.Runtime]System.Object 3 | { 4 | string) = ( 01 00 15 52 75 6E 74 69 6D 65 4E 75 6C 6C 61 62 // ...RuntimeNullab 5 | 6C 65 73 2E 46 6F 64 79 05 31 2E 30 2E 30 00 00 ) // les.Fody.1.0.0.. 6 | .class auto ansi sealed nested public SomeDelegate 7 | extends [System.Runtime]System.MulticastDelegate 8 | { 9 | .method public hidebysig specialname rtspecialname 10 | instance void .ctor(object 'object', 11 | native int 'method') runtime managed 12 | { 13 | } 14 | .method public hidebysig newslot virtual 15 | instance void Invoke(object obj) runtime managed 16 | { 17 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 18 | } 19 | .method public hidebysig newslot virtual 20 | instance class [System.Runtime]System.IAsyncResult 21 | BeginInvoke(object obj, 22 | class [System.Runtime]System.AsyncCallback callback, 23 | object 'object') runtime managed 24 | { 25 | .param [1] 26 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 27 | } 28 | .method public hidebysig newslot virtual 29 | instance void EndInvoke(class [System.Runtime]System.IAsyncResult result) runtime managed 30 | { 31 | } 32 | } 33 | .method public hidebysig static void Method(object obj) cil managed 34 | { 35 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 36 | .maxstack 8 37 | IL_0000: nop 38 | IL_0001: ret 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.PublicBaseClass.Release.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi beforefieldinit TestAssemblyNonPublic.PublicBaseClass 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig static string 7 | UncheckedBadReferenceReturn() cil managed 8 | { 9 | .maxstack 8 10 | IL_0000: ldnull 11 | IL_0001: ret 12 | } 13 | .method public hidebysig newslot abstract virtual 14 | instance void AbstractPublicMethod(string 'value') cil managed 15 | { 16 | } 17 | .method public hidebysig newslot virtual 18 | instance void VirtualPublicMethod(string 'value') cil managed 19 | { 20 | .maxstack 8 21 | IL_0000: ldarg.1 22 | IL_0001: brtrue.s IL_000d 23 | IL_0003: ldstr "value" 24 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 25 | IL_000d: ret 26 | } 27 | .method assembly hidebysig newslot abstract strict virtual 28 | instance void AbstractInternalMethod(string 'value') cil managed 29 | { 30 | } 31 | .method assembly hidebysig newslot strict virtual 32 | instance void VirtualInternalMethod(string 'value') cil managed 33 | { 34 | .maxstack 8 35 | IL_0000: ret 36 | } 37 | .method family hidebysig specialname rtspecialname 38 | instance void .ctor() cil managed 39 | { 40 | .maxstack 8 41 | IL_0000: ldarg.0 42 | IL_0001: call instance void [System.Runtime]System.Object::.ctor() 43 | IL_0006: ret 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.PublicBaseClass.Release.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi beforefieldinit TestAssemblyNonPublic.PublicBaseClass 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig static string 7 | UncheckedBadReferenceReturn() cil managed 8 | { 9 | .maxstack 8 10 | IL_0000: ldnull 11 | IL_0001: ret 12 | } 13 | .method public hidebysig newslot abstract virtual 14 | instance void AbstractPublicMethod(string 'value') cil managed 15 | { 16 | } 17 | .method public hidebysig newslot virtual 18 | instance void VirtualPublicMethod(string 'value') cil managed 19 | { 20 | .maxstack 8 21 | IL_0000: ldarg.1 22 | IL_0001: brtrue.s IL_000d 23 | IL_0003: ldstr "value" 24 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 25 | IL_000d: ret 26 | } 27 | .method assembly hidebysig newslot abstract strict virtual 28 | instance void AbstractInternalMethod(string 'value') cil managed 29 | { 30 | } 31 | .method assembly hidebysig newslot strict virtual 32 | instance void VirtualInternalMethod(string 'value') cil managed 33 | { 34 | .maxstack 8 35 | IL_0000: ret 36 | } 37 | .method family hidebysig specialname rtspecialname 38 | instance void .ctor() cil managed 39 | { 40 | .maxstack 8 41 | IL_0000: ldarg.0 42 | IL_0001: call instance void [System.Runtime]System.Object::.ctor() 43 | IL_0006: ret 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/TestAssembly/AsyncEnumerables.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task 5 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 6 | 7 | namespace TestAssembly; 8 | 9 | public static class AsyncEnumerables 10 | { 11 | public static async IAsyncEnumerable GoodGetReferencesAsync() 12 | { 13 | await Task.Delay(10); 14 | yield return string.Empty; 15 | } 16 | 17 | public static async IAsyncEnumerable BadGetReferencesAsync() 18 | { 19 | await Task.Delay(10); 20 | yield return string.Empty; 21 | yield return null!; 22 | yield return string.Empty; 23 | } 24 | 25 | public static async IAsyncEnumerable GoodGetReferencesAsyncWithoutAwait() 26 | { 27 | yield return string.Empty; 28 | } 29 | 30 | public static async IAsyncEnumerable BadGetReferencesAsyncWithoutAwait() 31 | { 32 | yield return string.Empty; 33 | yield return null!; 34 | yield return string.Empty; 35 | } 36 | 37 | public static async IAsyncEnumerable GoodGetGenericsAsync() where T : notnull, new() 38 | { 39 | await Task.Delay(10); 40 | yield return new T(); 41 | } 42 | 43 | public static async IAsyncEnumerable BadGetGenericsAsync() where T : notnull, new() 44 | { 45 | await Task.Delay(10); 46 | yield return new T(); 47 | yield return default!; 48 | yield return new T(); 49 | } 50 | } -------------------------------------------------------------------------------- /Source/TestAssembly/AsyncEnumerators.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task 5 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 6 | 7 | namespace TestAssembly; 8 | 9 | public static class AsyncEnumerators 10 | { 11 | public static async IAsyncEnumerator GoodGetReferencesAsync() 12 | { 13 | await Task.Delay(10); 14 | yield return string.Empty; 15 | } 16 | 17 | public static async IAsyncEnumerator BadGetReferencesAsync() 18 | { 19 | await Task.Delay(10); 20 | yield return string.Empty; 21 | yield return null!; 22 | yield return string.Empty; 23 | } 24 | 25 | public static async IAsyncEnumerator GoodGetReferencesAsyncWithoutAwait() 26 | { 27 | yield return string.Empty; 28 | } 29 | 30 | public static async IAsyncEnumerator BadGetReferencesAsyncWithoutAwait() 31 | { 32 | yield return string.Empty; 33 | yield return null!; 34 | yield return string.Empty; 35 | } 36 | 37 | public static async IAsyncEnumerator GoodGetGenericsAsync() where T : notnull, new() 38 | { 39 | await Task.Delay(10); 40 | yield return new T(); 41 | } 42 | 43 | public static async IAsyncEnumerator BadGetGenericsAsync() where T : notnull, new() 44 | { 45 | await Task.Delay(10); 46 | yield return new T(); 47 | yield return default!; 48 | yield return new T(); 49 | } 50 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/OutputParameterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestAssembly; 4 | 5 | namespace RuntimeNullables.Fody.Tests; 6 | 7 | [TestClass] 8 | public class OutputParameterTests 9 | { 10 | [TestMethod] 11 | public void GoodReference() => OutputParameters.GoodReference(out _); 12 | 13 | [TestMethod] 14 | public void GoodReferenceWithTryFinally() => OutputParameters.GoodReferenceWithTryFinally(out _); 15 | 16 | [TestMethod] 17 | public void BadReference() => Assert.ThrowsException(() => OutputParameters.BadReference(out _)); 18 | 19 | [TestMethod] 20 | public void NullableReference() => OutputParameters.NullableReference(out _); 21 | 22 | [TestMethod] 23 | public void GoodGenericNotNullConstraint() => OutputParameters.GoodGenericNotNullConstraint(out _); 24 | 25 | [TestMethod] 26 | public void BadGenericNotNull() => Assert.ThrowsException(() => OutputParameters.BadGenericNotNullConstraint(out _)); 27 | 28 | [TestMethod] 29 | public void GoodGenericNotNullAttribute() => OutputParameters.GoodGenericNotNullAttribute(out _); 30 | 31 | [TestMethod] 32 | public void BadGenericNotNullAttribute() => Assert.ThrowsException(() => OutputParameters.BadGenericNotNullAttribute(out _)); 33 | 34 | [TestMethod] 35 | public void GenericMaybeNullAttribute() => OutputParameters.GenericMaybeNullAttribute(out _); 36 | 37 | [TestMethod] 38 | public void GenericUnconstrained() => OutputParameters.GenericUnconstrained(out _); 39 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/ReturnTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestAssembly; 4 | 5 | namespace RuntimeNullables.Fody.Tests; 6 | 7 | [TestClass] 8 | public class ReturnTests 9 | { 10 | [TestMethod] 11 | public void GoodReference() => Returns.GoodReference(); 12 | 13 | [TestMethod] 14 | public void BadReference() => Assert.ThrowsException(Returns.BadReference); 15 | 16 | [TestMethod] 17 | public void NullableReference() => Returns.NullableReference(); 18 | 19 | [TestMethod] 20 | public void NoNullChecksBadReference() => Returns.NoNullChecksBadReference(); 21 | 22 | [TestMethod] 23 | public void NullChecksOnNoNullChecksMethodBadReference() => Assert.ThrowsException(Returns.NullChecksOnNoNullChecksMethodBadReference); 24 | 25 | [TestMethod] 26 | public void GoodGenericNotNullConstraint() => Returns.GoodGenericNotNullConstraint(); 27 | 28 | [TestMethod] 29 | public void BadGenericNotNull() => Assert.ThrowsException(Returns.BadGenericNotNullConstraint); 30 | 31 | [TestMethod] 32 | public void GoodGenericNotNullAttribute() => Returns.GoodGenericNotNullAttribute(); 33 | 34 | [TestMethod] 35 | public void BadGenericNotNullAttribute() => Assert.ThrowsException(Returns.BadGenericNotNullAttribute); 36 | 37 | [TestMethod] 38 | public void GenericMaybeNullAttribute() => Returns.GenericMaybeNullAttribute(); 39 | 40 | [TestMethod] 41 | public void GenericUnconstrained() => Returns.GenericUnconstrained(); 42 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/NonPublicTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestAssemblyNonPublic; 4 | 5 | namespace RuntimeNullables.Fody.Tests; 6 | 7 | [TestClass] 8 | public class NonPublicTests 9 | { 10 | [TestMethod] 11 | public void NoChecksOnAbstractInternalOnly() 12 | { 13 | var instance = new InternalSubClass(); 14 | instance.AbstractInternalMethod(null!); 15 | } 16 | 17 | [TestMethod] 18 | public void NoChecksOnVirtualInternalOnly() 19 | { 20 | var instance = new InternalSubClass(); 21 | instance.VirtualInternalMethod(null!); 22 | } 23 | 24 | [TestMethod] 25 | public void NoChecksOnInterfaceInternalOnly() 26 | { 27 | var instance = (IInternalInterface)new InternalSubClass(); 28 | instance.InterfaceInternalMethod(null!); 29 | } 30 | 31 | [TestMethod] 32 | public void ChecksOnOverrideOfAbstractPublicMethod() 33 | { 34 | var instance = new InternalSubClass(); 35 | Assert.ThrowsException(() => instance.AbstractPublicMethod(null!)); 36 | } 37 | 38 | [TestMethod] 39 | public void ChecksOnOverrideOfVirtualPublicMethod() 40 | { 41 | var instance = new InternalSubClass(); 42 | Assert.ThrowsException(() => instance.VirtualPublicMethod(null!)); 43 | } 44 | 45 | [TestMethod] 46 | public void ChecksOnOverrideOfInterfacePublicMethod() 47 | { 48 | var instance = (IPublicInterface)new InternalSubClass(); 49 | Assert.ThrowsException(() => instance.InterfacePublicMethod(null!)); 50 | } 51 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.NestedContainer.Release.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.NestedContainer 2 | extends [System.Runtime]System.Object 3 | { 4 | .class abstract auto ansi sealed nested public beforefieldinit Nested 5 | extends [System.Runtime]System.Object 6 | { 7 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 8 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 9 | .class abstract auto ansi sealed nested public beforefieldinit NestedDeeper 10 | extends [System.Runtime]System.Object 11 | { 12 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 13 | .method public hidebysig static void 14 | Unchecked(string 'value') cil managed 15 | { 16 | .maxstack 8 17 | IL_0000: ret 18 | } 19 | .method public hidebysig static void 20 | Checked(string 'value') cil managed 21 | { 22 | .maxstack 8 23 | IL_0000: ldarg.0 24 | IL_0001: brtrue.s IL_000d 25 | IL_0003: ldstr "value" 26 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 27 | IL_000d: ret 28 | } 29 | } 30 | .method public hidebysig static void 31 | Unchecked(string 'value') cil managed 32 | { 33 | .maxstack 8 34 | IL_0000: ret 35 | } 36 | .method public hidebysig static void 37 | Checked(string 'value') cil managed 38 | { 39 | .maxstack 8 40 | IL_0000: ldarg.0 41 | IL_0001: brtrue.s IL_000d 42 | IL_0003: ldstr "value" 43 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 44 | IL_000d: ret 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.NestedContainer.Release.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.NestedContainer 2 | extends [System.Runtime]System.Object 3 | { 4 | .class abstract auto ansi sealed nested public beforefieldinit Nested 5 | extends [System.Runtime]System.Object 6 | { 7 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 8 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 9 | .class abstract auto ansi sealed nested public beforefieldinit NestedDeeper 10 | extends [System.Runtime]System.Object 11 | { 12 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 13 | .method public hidebysig static void 14 | Unchecked(string 'value') cil managed 15 | { 16 | .maxstack 8 17 | IL_0000: ret 18 | } 19 | .method public hidebysig static void 20 | Checked(string 'value') cil managed 21 | { 22 | .maxstack 8 23 | IL_0000: ldarg.0 24 | IL_0001: brtrue.s IL_000d 25 | IL_0003: ldstr "value" 26 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 27 | IL_000d: ret 28 | } 29 | } 30 | .method public hidebysig static void 31 | Unchecked(string 'value') cil managed 32 | { 33 | .maxstack 8 34 | IL_0000: ret 35 | } 36 | .method public hidebysig static void 37 | Checked(string 'value') cil managed 38 | { 39 | .maxstack 8 40 | IL_0000: ldarg.0 41 | IL_0001: brtrue.s IL_000d 42 | IL_0003: ldstr "value" 43 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 44 | IL_000d: ret 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.PublicBaseClass.Debug.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi beforefieldinit TestAssemblyNonPublic.PublicBaseClass 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig static string 7 | UncheckedBadReferenceReturn() cil managed 8 | { 9 | .maxstack 8 10 | IL_0000: ldnull 11 | IL_0001: ret 12 | } 13 | .method public hidebysig newslot abstract virtual 14 | instance void AbstractPublicMethod(string 'value') cil managed 15 | { 16 | } 17 | .method public hidebysig newslot virtual 18 | instance void VirtualPublicMethod(string 'value') cil managed 19 | { 20 | .maxstack 8 21 | IL_0000: ldarg.1 22 | IL_0001: brtrue.s IL_000d 23 | IL_0003: ldstr "value" 24 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 25 | IL_000d: nop 26 | IL_000e: ret 27 | } 28 | .method assembly hidebysig newslot abstract strict virtual 29 | instance void AbstractInternalMethod(string 'value') cil managed 30 | { 31 | } 32 | .method assembly hidebysig newslot strict virtual 33 | instance void VirtualInternalMethod(string 'value') cil managed 34 | { 35 | .maxstack 8 36 | IL_0000: nop 37 | IL_0001: ret 38 | } 39 | .method family hidebysig specialname rtspecialname 40 | instance void .ctor() cil managed 41 | { 42 | .maxstack 8 43 | IL_0000: ldarg.0 44 | IL_0001: call instance void [System.Runtime]System.Object::.ctor() 45 | IL_0006: nop 46 | IL_0007: ret 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.PublicBaseClass.Debug.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi beforefieldinit TestAssemblyNonPublic.PublicBaseClass 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig static string 7 | UncheckedBadReferenceReturn() cil managed 8 | { 9 | .maxstack 8 10 | IL_0000: ldnull 11 | IL_0001: ret 12 | } 13 | .method public hidebysig newslot abstract virtual 14 | instance void AbstractPublicMethod(string 'value') cil managed 15 | { 16 | } 17 | .method public hidebysig newslot virtual 18 | instance void VirtualPublicMethod(string 'value') cil managed 19 | { 20 | .maxstack 8 21 | IL_0000: ldarg.1 22 | IL_0001: brtrue.s IL_000d 23 | IL_0003: ldstr "value" 24 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 25 | IL_000d: nop 26 | IL_000e: ret 27 | } 28 | .method assembly hidebysig newslot abstract strict virtual 29 | instance void AbstractInternalMethod(string 'value') cil managed 30 | { 31 | } 32 | .method assembly hidebysig newslot strict virtual 33 | instance void VirtualInternalMethod(string 'value') cil managed 34 | { 35 | .maxstack 8 36 | IL_0000: nop 37 | IL_0001: ret 38 | } 39 | .method family hidebysig specialname rtspecialname 40 | instance void .ctor() cil managed 41 | { 42 | .maxstack 8 43 | IL_0000: ldarg.0 44 | IL_0001: call instance void [System.Runtime]System.Object::.ctor() 45 | IL_0006: nop 46 | IL_0007: ret 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/ReturnBlockInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Mono.Cecil; 3 | using Mono.Cecil.Cil; 4 | using Mono.Collections.Generic; 5 | using BclCollections = System.Collections.Generic; 6 | 7 | namespace RuntimeNullables.Fody; 8 | 9 | internal class ReturnBlockInfo 10 | { 11 | public Instruction[] NewStartPoints { get; } 12 | 13 | public BclCollections.IReadOnlyList OldStartPoints { get; } 14 | 15 | /// 16 | /// Gets a list of branch instructions within the return block that should not be updated. 17 | /// 18 | public Collection Branches { get; } 19 | 20 | /// 21 | /// Initializes a new instance of the class for the given method, optionally setting the start points to the instruction 22 | /// previous to the return instruction. 23 | /// Creates. 24 | /// 25 | public ReturnBlockInfo(MethodDefinition method) 26 | { 27 | NewStartPoints = method.Body.Instructions.Where(i => i.OpCode == OpCodes.Ret).ToArray(); 28 | OldStartPoints = (Instruction[])NewStartPoints.Clone(); 29 | 30 | Branches = new Collection(); 31 | } 32 | 33 | /// 34 | /// Creates a branch instruction and adds it to the list of branch instructions. All branching instructions injected into a return block by the weaver 35 | /// should be created with this method. 36 | /// 37 | public Instruction CreateBranchInstruction(OpCode opcode, Instruction operand) 38 | { 39 | var instruction = Instruction.Create(opcode, operand); 40 | Branches.Add(instruction); 41 | return instruction; 42 | } 43 | } -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: build and test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | debug: 11 | 12 | runs-on: windows-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@v3 18 | with: 19 | dotnet-version: | 20 | 6.0.x 21 | 7.0.x 22 | 8.0.x 23 | - name: Clean 24 | run: dotnet clean --configuration Debug && dotnet nuget locals all --clear 25 | working-directory: Source 26 | - name: Install dependencies 27 | run: dotnet restore 28 | working-directory: Source 29 | - name: Build 30 | run: dotnet build --configuration Debug -p:IsCI=true --no-restore 31 | working-directory: Source 32 | - name: Test 33 | run: dotnet test --configuration Debug --no-build --verbosity normal 34 | working-directory: Source 35 | 36 | release: 37 | 38 | runs-on: windows-latest 39 | 40 | steps: 41 | - uses: actions/checkout@v3 42 | - name: Setup .NET 43 | uses: actions/setup-dotnet@v3 44 | with: 45 | dotnet-version: | 46 | 6.0.x 47 | 7.0.x 48 | 8.0.x 49 | - name: Clean 50 | run: dotnet clean --configuration Release && dotnet nuget locals all --clear 51 | working-directory: Source 52 | - name: Install dependencies 53 | run: dotnet restore 54 | working-directory: Source 55 | - name: Build 56 | run: dotnet build --configuration Release -p:IsCI=true --no-restore 57 | working-directory: Source 58 | - name: Test 59 | run: dotnet test --configuration Release --no-build --verbosity normal 60 | working-directory: Source -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Extensions/TypeReferenceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Mono.Cecil; 2 | 3 | namespace RuntimeNullables.Fody.Extensions; 4 | 5 | internal static class TypeReferenceExtensions 6 | { 7 | public static bool IsReferenceType(this TypeReference type) 8 | { 9 | if (type.IsPointer) 10 | return false; 11 | 12 | if (type.IsArray) 13 | return true; 14 | 15 | type = type.GetElementType(); 16 | return !(type.IsValueType || (type is GenericParameter param && param.HasNotNullableValueTypeConstraint)); 17 | } 18 | 19 | public static bool IsNonGenericTaskType(this TypeReference type) 20 | { 21 | return type.GetElementType()?.FullName is "System.Threading.Tasks.Task" or "System.Threading.Tasks.ValueTask"; 22 | } 23 | 24 | public static bool IsTaskWithResultType(this TypeReference type) 25 | { 26 | return type.GetElementType()?.FullName is "System.Threading.Tasks.Task`1" or "System.Threading.Tasks.ValueTask`1"; 27 | } 28 | 29 | public static bool IsNonGenericEnumeratorType(this TypeReference type) 30 | { 31 | return type.GetElementType()?.FullName is "System.Collections.IEnumerable" or "System.Collections.IEnumerator"; 32 | } 33 | 34 | public static bool IsGenericEnumeratorType(this TypeReference type) 35 | { 36 | return type.GetElementType()?.FullName is "System.Collections.Generic.IEnumerable`1" or "System.Collections.Generic.IEnumerator`1"; 37 | } 38 | 39 | public static bool IsAsyncEnumeratorType(this TypeReference type) 40 | { 41 | return type.GetElementType()?.FullName is "System.Collections.Generic.IAsyncEnumerable`1" or "System.Collections.Generic.IAsyncEnumerator`1"; 42 | } 43 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/AsyncEnumerableTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using TestAssembly; 5 | 6 | namespace RuntimeNullables.Fody.Tests; 7 | 8 | [TestClass] 9 | public class AsyncEnumerableTests 10 | { 11 | [TestMethod] 12 | public async Task GoodGetReferencesAsync() 13 | { 14 | await foreach (var value in AsyncEnumerables.GoodGetReferencesAsync()) { } 15 | } 16 | 17 | [TestMethod] 18 | public Task BadGetReferencesAsync() 19 | { 20 | return Assert.ThrowsExceptionAsync(async () => { 21 | await foreach (var value in AsyncEnumerables.BadGetReferencesAsync()) { } 22 | }); 23 | } 24 | 25 | [TestMethod] 26 | public async Task GoodGetReferencesAsyncWithoutAwait() 27 | { 28 | await foreach (var value in AsyncEnumerables.GoodGetReferencesAsyncWithoutAwait()) { } 29 | } 30 | 31 | [TestMethod] 32 | public Task BadGetReferencesAsyncWithoutAwait() 33 | { 34 | return Assert.ThrowsExceptionAsync(async () => { 35 | await foreach (var value in AsyncEnumerables.BadGetReferencesAsyncWithoutAwait()) { } 36 | }); 37 | } 38 | 39 | [TestMethod] 40 | public async Task GoodGetGenericsAsync() 41 | { 42 | await foreach (var value in AsyncEnumerables.GoodGetGenericsAsync()) { } 43 | } 44 | 45 | [TestMethod] 46 | public Task BadGetGenericsAsync() 47 | { 48 | return Assert.ThrowsExceptionAsync(async () => { 49 | await foreach (var value in AsyncEnumerables.BadGetGenericsAsync()) { } 50 | }); 51 | } 52 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyPETests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Fody; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace RuntimeNullables.Fody.Tests; 6 | 7 | // TODO: Figure out why this fails in CI, ignore for now. 8 | #if !CI 9 | [TestClass] 10 | #endif 11 | public class VerifyPETests 12 | { 13 | // This will effectively "double up" the null checks since the assembly has already been processed. 14 | // IL should still verify afterwards - helps ensure we are doing sensible stack management in our injected IL. 15 | 16 | [TestMethod] 17 | public void VerifyTestAssembly() 18 | { 19 | var weaver = new ModuleWeaver() { DefineConstants = new List { "DEBUG" } }; 20 | weaver.ExecuteTestRun("TestAssembly.dll", ignoreCodes: new[] { "0x80131205", "0x80131884" }); 21 | } 22 | 23 | [TestMethod] 24 | public void VerifyTestAssemblyPointers() 25 | { 26 | var weaver = new ModuleWeaver() { DefineConstants = new List { "DEBUG" } }; 27 | weaver.ExecuteTestRun("TestAssemblyNonPublic.dll", ignoreCodes: new[] { "0x80131205", "0x801318DE" }); 28 | } 29 | 30 | [TestMethod] 31 | public void VerifyTestAssemblyNonPublic() 32 | { 33 | var weaver = new ModuleWeaver() { DefineConstants = new List { "DEBUG" } }; 34 | weaver.ExecuteTestRun("TestAssemblyNonPublic.dll", ignoreCodes: new[] { "0x80131205" }); 35 | } 36 | 37 | [TestMethod] 38 | public void VerifyTestAssemblyWithThrowHelpers() 39 | { 40 | var weaver = new ModuleWeaver() { DefineConstants = new List { "DEBUG" } }; 41 | weaver.ExecuteTestRun("TestAssemblyThrowHelpers.dll", ignoreCodes: new[] { "0x80131205" }); 42 | } 43 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/ComboAndMessageTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestAssembly; 4 | 5 | namespace RuntimeNullables.Fody.Tests; 6 | 7 | [TestClass] 8 | public class ComboAndMessageTests 9 | { 10 | [TestMethod] 11 | public void GoodInputAndOutput() 12 | { 13 | var instance = new Combo(); 14 | object refValue = string.Empty; 15 | 16 | instance.GetValue(false, string.Empty, ref refValue, false); 17 | } 18 | 19 | [TestMethod] 20 | public void BadInput() 21 | { 22 | var instance = new Combo(); 23 | 24 | object refValue = string.Empty; 25 | var ex = Assert.ThrowsException(() => instance.GetValue(false, null!, ref refValue, false)); 26 | Assert.AreEqual("inputValue", ex.ParamName); 27 | 28 | refValue = null; 29 | ex = Assert.ThrowsException(() => instance.GetValue(false, string.Empty, ref refValue!, false)); 30 | Assert.AreEqual("refValue", ex.ParamName); 31 | } 32 | 33 | [TestMethod] 34 | public void BadOutput() 35 | { 36 | var instance = new Combo(); 37 | object refValue = string.Empty; 38 | 39 | var ex = Assert.ThrowsException(() => instance.GetValue(true, string.Empty, ref refValue, false)); 40 | Assert.AreEqual("Return value nullability contract was broken.", ex.Message); 41 | 42 | ex = Assert.ThrowsException(() => instance.GetValue(false, string.Empty, ref refValue, true)); 43 | Assert.AreEqual("Output parameter 'refValue' nullability contract was broken.", ex.Message); 44 | } 45 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.NestedContainer.Debug.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.NestedContainer 2 | extends [System.Runtime]System.Object 3 | { 4 | .class abstract auto ansi sealed nested public beforefieldinit Nested 5 | extends [System.Runtime]System.Object 6 | { 7 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 8 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 9 | .class abstract auto ansi sealed nested public beforefieldinit NestedDeeper 10 | extends [System.Runtime]System.Object 11 | { 12 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 13 | .method public hidebysig static void 14 | Unchecked(string 'value') cil managed 15 | { 16 | .maxstack 8 17 | IL_0000: nop 18 | IL_0001: ret 19 | } 20 | .method public hidebysig static void 21 | Checked(string 'value') cil managed 22 | { 23 | .maxstack 8 24 | IL_0000: ldarg.0 25 | IL_0001: brtrue.s IL_000d 26 | IL_0003: ldstr "value" 27 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 28 | IL_000d: nop 29 | IL_000e: ret 30 | } 31 | } 32 | .method public hidebysig static void 33 | Unchecked(string 'value') cil managed 34 | { 35 | .maxstack 8 36 | IL_0000: nop 37 | IL_0001: ret 38 | } 39 | .method public hidebysig static void 40 | Checked(string 'value') cil managed 41 | { 42 | .maxstack 8 43 | IL_0000: ldarg.0 44 | IL_0001: brtrue.s IL_000d 45 | IL_0003: ldstr "value" 46 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 47 | IL_000d: nop 48 | IL_000e: ret 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.NestedContainer.Debug.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.NestedContainer 2 | extends [System.Runtime]System.Object 3 | { 4 | .class abstract auto ansi sealed nested public beforefieldinit Nested 5 | extends [System.Runtime]System.Object 6 | { 7 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 8 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 9 | .class abstract auto ansi sealed nested public beforefieldinit NestedDeeper 10 | extends [System.Runtime]System.Object 11 | { 12 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 13 | .method public hidebysig static void 14 | Unchecked(string 'value') cil managed 15 | { 16 | .maxstack 8 17 | IL_0000: nop 18 | IL_0001: ret 19 | } 20 | .method public hidebysig static void 21 | Checked(string 'value') cil managed 22 | { 23 | .maxstack 8 24 | IL_0000: ldarg.0 25 | IL_0001: brtrue.s IL_000d 26 | IL_0003: ldstr "value" 27 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 28 | IL_000d: nop 29 | IL_000e: ret 30 | } 31 | } 32 | .method public hidebysig static void 33 | Unchecked(string 'value') cil managed 34 | { 35 | .maxstack 8 36 | IL_0000: nop 37 | IL_0001: ret 38 | } 39 | .method public hidebysig static void 40 | Checked(string 'value') cil managed 41 | { 42 | .maxstack 8 43 | IL_0000: ldarg.0 44 | IL_0001: brtrue.s IL_000d 45 | IL_0003: ldstr "value" 46 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 47 | IL_000d: nop 48 | IL_000e: ret 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/ILHelpers.cs: -------------------------------------------------------------------------------- 1 | using Mono.Cecil; 2 | using Mono.Cecil.Cil; 3 | using Mono.Collections.Generic; 4 | 5 | namespace RuntimeNullables.Fody; 6 | 7 | internal static class ILHelpers 8 | { 9 | /// 10 | /// Converts the value on the stack to a value ref which can be used to test for null. 11 | /// 12 | public static void InsertGetValueRef(ref int index, Collection instructions, TypeReference valueType) 13 | { 14 | var elementType = valueType.GetElementType(); 15 | 16 | if (valueType.IsByReference) { 17 | if (elementType.IsGenericParameter) { 18 | instructions.Insert(index++, Instruction.Create(OpCodes.Ldobj, elementType)); 19 | instructions.Insert(index++, Instruction.Create(OpCodes.Box, elementType)); 20 | } 21 | else { 22 | instructions.Insert(index++, Instruction.Create(OpCodes.Ldind_Ref)); 23 | } 24 | } 25 | else if (elementType.IsGenericParameter) { 26 | instructions.Insert(index++, Instruction.Create(OpCodes.Box, valueType)); 27 | } 28 | } 29 | 30 | public static void InsertThrowHelperCallIfValueRefIsNull( 31 | ref int index, 32 | Collection instructions, 33 | MethodReference throwHelperMethod, 34 | string paramValue, 35 | Instruction continuationPoint, 36 | ReturnBlockInfo? returnBlockInfo = null) 37 | { 38 | var branchIfNotNull = Instruction.Create(OpCodes.Brtrue_S, continuationPoint); 39 | returnBlockInfo?.Branches.Add(branchIfNotNull); 40 | instructions.Insert(index++, branchIfNotNull); 41 | 42 | instructions.Insert(index++, Instruction.Create(OpCodes.Ldstr, paramValue)); 43 | instructions.Insert(index++, Instruction.Create(OpCodes.Call, throwHelperMethod)); 44 | } 45 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Contexts/ModuleContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Mono.Cecil; 4 | 5 | namespace RuntimeNullables.Fody.Contexts; 6 | 7 | internal sealed class ModuleContext : NullableContext 8 | { 9 | private readonly Dictionary _typeContexts = new(); 10 | 11 | public ModuleDefinition Module { get; } 12 | 13 | public IReadOnlyDictionary TypeContexts => _typeContexts; 14 | 15 | public ModuleContext(ModuleDefinition module, WeavingContext weavingContext) : base(module, new AssemblyContext(module.Assembly, weavingContext)) 16 | { 17 | Module = module; 18 | } 19 | 20 | public override void Build() 21 | { 22 | base.Build(); 23 | 24 | foreach (var type in Module.GetTypes()) 25 | Build(type); 26 | 27 | TypeContext? Build(TypeDefinition type) 28 | { 29 | if (_typeContexts.TryGetValue(type, out var typeContext)) 30 | return typeContext; 31 | 32 | if (type.HasAnyGeneratedAttribute() || IsInjectedType(type)) 33 | return null; 34 | 35 | if (type.DeclaringType == null) { 36 | typeContext = new TypeContext(type, this); 37 | } 38 | else { 39 | var declaringTypeContext = Build(type.DeclaringType); 40 | 41 | if (declaringTypeContext == null) 42 | return null; 43 | 44 | typeContext = new TypeContext(type, declaringTypeContext); 45 | } 46 | 47 | typeContext.Build(); 48 | _typeContexts.Add(type, typeContext); 49 | 50 | return typeContext; 51 | } 52 | 53 | static bool IsInjectedType(TypeDefinition type) 54 | { 55 | return type.Namespace is "RuntimeNullables" or "System.Runtime.CompilerServices" or "System.Diagnostics.CodeAnalysis"; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Source/TestAssembly/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Determines whether outputs (return values and out/ref parameters) have null checks injected. 12 | 13 | 14 | 15 | 16 | Determines whether non-public members have null checks injected or if only public entry points are checked. 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 25 | 26 | 27 | 28 | 29 | A comma-separated list of error codes that can be safely ignored in assembly verification. 30 | 31 | 32 | 33 | 34 | 'false' to turn off automatic generation of the XML Schema file. 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Source/TestAssemblyNonPublic/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Determines whether outputs (return values and out/ref parameters) have null checks injected. 12 | 13 | 14 | 15 | 16 | Determines whether non-public members have null checks injected or if only public entry points are checked. 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 25 | 26 | 27 | 28 | 29 | A comma-separated list of error codes that can be safely ignored in assembly verification. 30 | 31 | 32 | 33 | 34 | 'false' to turn off automatic generation of the XML Schema file. 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Source/TestAssemblyPointers/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Determines whether outputs (return values and out/ref parameters) have null checks injected. 12 | 13 | 14 | 15 | 16 | Determines whether non-public members have null checks injected or if only public entry points are checked. 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 25 | 26 | 27 | 28 | 29 | A comma-separated list of error codes that can be safely ignored in assembly verification. 30 | 31 | 32 | 33 | 34 | 'false' to turn off automatic generation of the XML Schema file. 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Source/TestAssemblyThrowHelpers/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Determines whether outputs (return values and out/ref parameters) have null checks injected. 12 | 13 | 14 | 15 | 16 | Determines whether non-public members have null checks injected or if only public entry points are checked. 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 25 | 26 | 27 | 28 | 29 | A comma-separated list of error codes that can be safely ignored in assembly verification. 30 | 31 | 32 | 33 | 34 | 'false' to turn off automatic generation of the XML Schema file. 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/AsyncEnumeratorTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using TestAssembly; 5 | 6 | #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task 7 | 8 | namespace RuntimeNullables.Fody.Tests; 9 | 10 | [TestClass] 11 | public class AsyncEnumeratorTests 12 | { 13 | [TestMethod] 14 | public async Task GoodGetReferencesAsync() 15 | { 16 | var enumerator = AsyncEnumerators.GoodGetReferencesAsync(); 17 | while (await enumerator.MoveNextAsync()) { } 18 | } 19 | 20 | [TestMethod] 21 | public Task BadGetReferencesAsync() 22 | { 23 | return Assert.ThrowsExceptionAsync(async () => { 24 | var enumerator = AsyncEnumerators.BadGetReferencesAsync(); 25 | while (await enumerator.MoveNextAsync()) { } 26 | }); 27 | } 28 | 29 | [TestMethod] 30 | public async Task GoodGetReferencesAsyncWithoutAwait() 31 | { 32 | var enumerator = AsyncEnumerators.GoodGetReferencesAsyncWithoutAwait(); 33 | while (await enumerator.MoveNextAsync()) { } 34 | } 35 | 36 | [TestMethod] 37 | public Task BadGetReferencesAsyncWithoutAwait() 38 | { 39 | return Assert.ThrowsExceptionAsync(async () => { 40 | var enumerator = AsyncEnumerators.BadGetReferencesAsyncWithoutAwait(); 41 | while (await enumerator.MoveNextAsync()) { } 42 | }); 43 | } 44 | 45 | [TestMethod] 46 | public async Task GoodGetGenericsAsync() 47 | { 48 | var enumerator = AsyncEnumerators.GoodGetGenericsAsync(); 49 | while (await enumerator.MoveNextAsync()) { } 50 | } 51 | 52 | [TestMethod] 53 | public Task BadGetGenericsAsync() 54 | { 55 | return Assert.ThrowsExceptionAsync(async () => { 56 | var enumerator = AsyncEnumerators.BadGetGenericsAsync(); 57 | while (await enumerator.MoveNextAsync()) { } 58 | }); 59 | } 60 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.Combo.Release.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public auto ansi beforefieldinit TestAssembly.Combo`1 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig instance string 7 | GetValue(bool returnNull, 8 | !TClass inputValue, 9 | !!TMethod& refValue, 10 | bool outputNull) cil managed 11 | { 12 | .maxstack 2 13 | IL_0000: ldarg.2 14 | IL_0001: box !TClass 15 | IL_0006: brtrue.s IL_0012 16 | IL_0008: ldstr "inputValue" 17 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 18 | IL_0012: ldarg.3 19 | IL_0013: ldobj !!TMethod 20 | IL_0018: box !!TMethod 21 | IL_001d: brtrue.s IL_0029 22 | IL_001f: ldstr "refValue" 23 | IL_0024: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 24 | IL_0029: ldarg.1 25 | IL_002a: brtrue.s IL_004c 26 | IL_002c: ldarga.s inputValue 27 | IL_002e: constrained. !TClass 28 | IL_0034: callvirt instance string [System.Runtime]System.Object::ToString() 29 | IL_0039: ldarg.3 30 | IL_003a: constrained. !!TMethod 31 | IL_0040: callvirt instance string [System.Runtime]System.Object::ToString() 32 | IL_0045: call string [System.Runtime]System.String::Concat(string, 33 | string) 34 | IL_004a: br.s IL_004d 35 | IL_004c: ldnull 36 | IL_004d: ldarg.s outputNull 37 | IL_004f: brfalse.s IL_0058 38 | IL_0051: ldarg.3 39 | IL_0052: initobj !!TMethod 40 | IL_0058: ldarg.3 41 | IL_0059: ldobj !!TMethod 42 | IL_005e: box !!TMethod 43 | IL_0063: brtrue.s IL_006f 44 | IL_0065: ldstr "Output parameter 'refValue' nullability contract w" 45 | + "as broken." 46 | IL_006a: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 47 | IL_006f: dup 48 | IL_0070: brtrue.s IL_007c 49 | IL_0072: ldstr "Return value nullability contract was broken." 50 | IL_0077: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 51 | IL_007c: ret 52 | } 53 | .method public hidebysig specialname rtspecialname 54 | instance void .ctor() cil managed 55 | { 56 | .maxstack 8 57 | IL_0000: ldarg.0 58 | IL_0001: call instance void [System.Runtime]System.Object::.ctor() 59 | IL_0006: ret 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.Combo.Release.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public auto ansi beforefieldinit TestAssembly.Combo`1 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig instance string 7 | GetValue(bool returnNull, 8 | !TClass inputValue, 9 | !!TMethod& refValue, 10 | bool outputNull) cil managed 11 | { 12 | .maxstack 2 13 | IL_0000: ldarg.2 14 | IL_0001: box !TClass 15 | IL_0006: brtrue.s IL_0012 16 | IL_0008: ldstr "inputValue" 17 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 18 | IL_0012: ldarg.3 19 | IL_0013: ldobj !!TMethod 20 | IL_0018: box !!TMethod 21 | IL_001d: brtrue.s IL_0029 22 | IL_001f: ldstr "refValue" 23 | IL_0024: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 24 | IL_0029: ldarg.1 25 | IL_002a: brtrue.s IL_004c 26 | IL_002c: ldarga.s inputValue 27 | IL_002e: constrained. !TClass 28 | IL_0034: callvirt instance string [System.Runtime]System.Object::ToString() 29 | IL_0039: ldarg.3 30 | IL_003a: constrained. !!TMethod 31 | IL_0040: callvirt instance string [System.Runtime]System.Object::ToString() 32 | IL_0045: call string [System.Runtime]System.String::Concat(string, 33 | string) 34 | IL_004a: br.s IL_004d 35 | IL_004c: ldnull 36 | IL_004d: ldarg.s outputNull 37 | IL_004f: brfalse.s IL_0058 38 | IL_0051: ldarg.3 39 | IL_0052: initobj !!TMethod 40 | IL_0058: ldarg.3 41 | IL_0059: ldobj !!TMethod 42 | IL_005e: box !!TMethod 43 | IL_0063: brtrue.s IL_006f 44 | IL_0065: ldstr "Output parameter 'refValue' nullability contract w" 45 | + "as broken." 46 | IL_006a: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 47 | IL_006f: dup 48 | IL_0070: brtrue.s IL_007c 49 | IL_0072: ldstr "Return value nullability contract was broken." 50 | IL_0077: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 51 | IL_007c: ret 52 | } 53 | .method public hidebysig specialname rtspecialname 54 | instance void .ctor() cil managed 55 | { 56 | .maxstack 8 57 | IL_0000: ldarg.0 58 | IL_0001: call instance void [System.Runtime]System.Object::.ctor() 59 | IL_0006: ret 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Contexts/WeavingContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RuntimeNullables.Fody.Contexts; 4 | 5 | internal class WeavingContext 6 | { 7 | private readonly ModuleWeaver _weaver; 8 | 9 | private BclReferences? _bclReferences; 10 | private ModuleReferences? _moduleReferences; 11 | 12 | public WeavingContext(ModuleWeaver weaver) 13 | { 14 | _weaver = weaver; 15 | ModuleContext = new ModuleContext(weaver.ModuleDefinition, this); 16 | 17 | bool isDebug = weaver.DefineConstants.Contains("DEBUG"); 18 | 19 | CheckOutputs = weaver.GetConfigBooleanValue("CheckOutputs") ?? isDebug; 20 | CheckNonPublic = weaver.GetConfigBooleanValue("CheckNonPublic") ?? isDebug; 21 | } 22 | 23 | public ModuleContext ModuleContext { get; } 24 | 25 | public BclReferences BclReferences => _bclReferences ?? throw new InvalidOperationException("Weaving context not built."); 26 | 27 | public ModuleReferences ModuleReferences { 28 | get => _moduleReferences ?? throw new InvalidOperationException("Module references not set."); 29 | set => _moduleReferences = value; 30 | } 31 | 32 | public bool CheckOutputs { get; } 33 | 34 | public bool CheckNonPublic { get; } 35 | 36 | public void WriteInfo(string message) => _weaver.WriteInfo(message); 37 | 38 | public void WriteWarning(string message) => _weaver.WriteWarning(message); 39 | 40 | public void WriteError(string message) => _weaver.WriteError(message); 41 | 42 | public void Build() 43 | { 44 | _bclReferences = new BclReferences(_weaver); 45 | ModuleContext.Build(); 46 | } 47 | 48 | /// 49 | /// Combines the result of two exclusive annotation presence checks and writes a warning if they are both present. 50 | /// 51 | /// True if trueResult == true or both result are true, false if falseResult == true, otherwise null. 52 | public bool? CombineExclusiveAnnotations(bool trueResult, bool falseResult, string warningFormat, object warningArg0, object? warningArg1 = null) 53 | { 54 | if (trueResult && falseResult) { 55 | string message = string.Format(null, warningFormat, warningArg0, warningArg1); 56 | WriteWarning(message); 57 | return true; 58 | } 59 | 60 | if (trueResult) 61 | return true; 62 | 63 | if (falseResult) 64 | return false; 65 | 66 | return null; 67 | } 68 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/InputParametersTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using TestAssembly; 4 | 5 | namespace RuntimeNullables.Fody.Tests; 6 | 7 | [TestClass] 8 | public class InputParametersTests 9 | { 10 | [TestMethod] 11 | public void Reference() 12 | { 13 | InputParameters.Reference(string.Empty); 14 | Assert.ThrowsException(() => InputParameters.Reference(null!)); 15 | } 16 | 17 | [TestMethod] 18 | public void NoNullChecksReference() 19 | { 20 | InputParameters.NoNullChecksReference(null!); 21 | } 22 | 23 | [TestMethod] 24 | public void NullableReference() 25 | { 26 | InputParameters.NullableReference(null); 27 | } 28 | 29 | [TestMethod] 30 | public void ValueArray() 31 | { 32 | InputParameters.ValueArray(new byte[0]); 33 | Assert.ThrowsException(() => InputParameters.ValueArray(null!)); 34 | } 35 | 36 | [TestMethod] 37 | public void GenericUnconstrained() 38 | { 39 | InputParameters.GenericUnconstrained(null); 40 | } 41 | 42 | [TestMethod] 43 | public void GenericUnconstrainedIn() 44 | { 45 | InputParameters.GenericUnconstrainedIn(null); 46 | } 47 | 48 | [TestMethod] 49 | public void GenericNotNullConstraint() 50 | { 51 | InputParameters.GenericNotNullConstraint(string.Empty); 52 | Assert.ThrowsException(() => InputParameters.GenericNotNullConstraint(null!)); 53 | } 54 | 55 | [TestMethod] 56 | public void GenericDisallowNull() 57 | { 58 | InputParameters.GenericDisallowNull(string.Empty); 59 | Assert.ThrowsException(() => InputParameters.GenericDisallowNull(null!)); 60 | } 61 | 62 | [TestMethod] 63 | public void GenericClassConstraint() 64 | { 65 | InputParameters.GenericClassConstraint(string.Empty); 66 | Assert.ThrowsException(() => InputParameters.GenericClassConstraint(null!)); 67 | } 68 | 69 | [TestMethod] 70 | public void GenericClassConstraintWithAllowNull() 71 | { 72 | InputParameters.GenericClassConstraintWithAllowNull(null); 73 | } 74 | 75 | [TestMethod] 76 | public void GenericClassConstraintWithNullable() 77 | { 78 | InputParameters.GenericClassConstraintWithNullable(null); 79 | } 80 | 81 | [TestMethod] 82 | public void GenericNullableClassConstraint() 83 | { 84 | InputParameters.GenericNullableClassConstraint(null!); 85 | } 86 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.Combo.Debug.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public auto ansi beforefieldinit TestAssembly.Combo`1 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig instance string 7 | GetValue(bool returnNull, 8 | !TClass inputValue, 9 | !!TMethod& refValue, 10 | bool outputNull) cil managed 11 | { 12 | .maxstack 2 13 | .locals init (string V_0, 14 | bool V_1, 15 | string V_2) 16 | IL_0000: ldarg.2 17 | IL_0001: box !TClass 18 | IL_0006: brtrue.s IL_0012 19 | IL_0008: ldstr "inputValue" 20 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 21 | IL_0012: ldarg.3 22 | IL_0013: ldobj !!TMethod 23 | IL_0018: box !!TMethod 24 | IL_001d: brtrue.s IL_0029 25 | IL_001f: ldstr "refValue" 26 | IL_0024: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 27 | IL_0029: nop 28 | IL_002a: ldarg.1 29 | IL_002b: brtrue.s IL_004d 30 | IL_002d: ldarga.s inputValue 31 | IL_002f: constrained. !TClass 32 | IL_0035: callvirt instance string [System.Runtime]System.Object::ToString() 33 | IL_003a: ldarg.3 34 | IL_003b: constrained. !!TMethod 35 | IL_0041: callvirt instance string [System.Runtime]System.Object::ToString() 36 | IL_0046: call string [System.Runtime]System.String::Concat(string, 37 | string) 38 | IL_004b: br.s IL_004e 39 | IL_004d: ldnull 40 | IL_004e: stloc.0 41 | IL_004f: ldarg.s outputNull 42 | IL_0051: stloc.1 43 | IL_0052: ldloc.1 44 | IL_0053: brfalse.s IL_005c 45 | IL_0055: ldarg.3 46 | IL_0056: initobj !!TMethod 47 | IL_005c: ldloc.0 48 | IL_005d: stloc.2 49 | IL_005e: br.s IL_0060 50 | IL_0060: ldloc.2 51 | IL_0061: ldarg.3 52 | IL_0062: ldobj !!TMethod 53 | IL_0067: box !!TMethod 54 | IL_006c: brtrue.s IL_0078 55 | IL_006e: ldstr "Output parameter 'refValue' nullability contract w" 56 | + "as broken." 57 | IL_0073: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 58 | IL_0078: dup 59 | IL_0079: brtrue.s IL_0085 60 | IL_007b: ldstr "Return value nullability contract was broken." 61 | IL_0080: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 62 | IL_0085: ret 63 | } 64 | .method public hidebysig specialname rtspecialname 65 | instance void .ctor() cil managed 66 | { 67 | .maxstack 8 68 | IL_0000: ldarg.0 69 | IL_0001: call instance void [System.Runtime]System.Object::.ctor() 70 | IL_0006: nop 71 | IL_0007: ret 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.Combo.Debug.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public auto ansi beforefieldinit TestAssembly.Combo`1 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig instance string 7 | GetValue(bool returnNull, 8 | !TClass inputValue, 9 | !!TMethod& refValue, 10 | bool outputNull) cil managed 11 | { 12 | .maxstack 2 13 | .locals init (string V_0, 14 | bool V_1, 15 | string V_2) 16 | IL_0000: ldarg.2 17 | IL_0001: box !TClass 18 | IL_0006: brtrue.s IL_0012 19 | IL_0008: ldstr "inputValue" 20 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 21 | IL_0012: ldarg.3 22 | IL_0013: ldobj !!TMethod 23 | IL_0018: box !!TMethod 24 | IL_001d: brtrue.s IL_0029 25 | IL_001f: ldstr "refValue" 26 | IL_0024: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 27 | IL_0029: nop 28 | IL_002a: ldarg.1 29 | IL_002b: brtrue.s IL_004d 30 | IL_002d: ldarga.s inputValue 31 | IL_002f: constrained. !TClass 32 | IL_0035: callvirt instance string [System.Runtime]System.Object::ToString() 33 | IL_003a: ldarg.3 34 | IL_003b: constrained. !!TMethod 35 | IL_0041: callvirt instance string [System.Runtime]System.Object::ToString() 36 | IL_0046: call string [System.Runtime]System.String::Concat(string, 37 | string) 38 | IL_004b: br.s IL_004e 39 | IL_004d: ldnull 40 | IL_004e: stloc.0 41 | IL_004f: ldarg.s outputNull 42 | IL_0051: stloc.1 43 | IL_0052: ldloc.1 44 | IL_0053: brfalse.s IL_005c 45 | IL_0055: ldarg.3 46 | IL_0056: initobj !!TMethod 47 | IL_005c: ldloc.0 48 | IL_005d: stloc.2 49 | IL_005e: br.s IL_0060 50 | IL_0060: ldloc.2 51 | IL_0061: ldarg.3 52 | IL_0062: ldobj !!TMethod 53 | IL_0067: box !!TMethod 54 | IL_006c: brtrue.s IL_0078 55 | IL_006e: ldstr "Output parameter 'refValue' nullability contract w" 56 | + "as broken." 57 | IL_0073: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 58 | IL_0078: dup 59 | IL_0079: brtrue.s IL_0085 60 | IL_007b: ldstr "Return value nullability contract was broken." 61 | IL_0080: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 62 | IL_0085: ret 63 | } 64 | .method public hidebysig specialname rtspecialname 65 | instance void .ctor() cil managed 66 | { 67 | .maxstack 8 68 | IL_0000: ldarg.0 69 | IL_0001: call instance void [System.Runtime]System.Object::.ctor() 70 | IL_0006: nop 71 | IL_0007: ret 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/ModuleWeaver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Xml; 4 | using Fody; 5 | using RuntimeNullables.Fody.Contexts; 6 | 7 | namespace RuntimeNullables.Fody; 8 | 9 | /// 10 | /// Provides the implementation of the weaving functionality. 11 | /// 12 | /// 13 | /// See https://github.com/dotnet/roslyn/blob/master/docs/features/nullable-metadata.md for complete details on nullable metadata. 14 | /// 15 | public partial class ModuleWeaver : BaseModuleWeaver 16 | { 17 | public override IEnumerable GetAssembliesForScanning() 18 | { 19 | yield return "mscorlib"; 20 | yield return "System.Runtime"; 21 | yield return "System"; 22 | yield return "netstandard"; 23 | yield return "System.Diagnostics.Tools"; 24 | } 25 | 26 | public override bool ShouldCleanReference => true; 27 | 28 | public override void Execute() 29 | { 30 | // Uncomment to attach debugger when msbuild task runs: 31 | // if (ModuleDefinition.Assembly.Name.Name == "TestAssembly") 32 | // Debugger.Launch(); 33 | 34 | var weavingContext = new WeavingContext(this); 35 | weavingContext.WriteInfo("Starting weaving context build."); 36 | weavingContext.Build(); 37 | 38 | weavingContext.WriteInfo($"Starting injection - CheckOutputs: {weavingContext.CheckOutputs}, CheckNonPublic: {weavingContext.CheckNonPublic}."); 39 | 40 | try { 41 | InjectThrowHelpers(weavingContext); 42 | } 43 | catch (Exception ex) { 44 | throw new RuntimeNullablesException("Error injecting throw helpers.", ex); 45 | } 46 | 47 | var moduleContext = weavingContext.ModuleContext; 48 | 49 | foreach (var typeContext in moduleContext.TypeContexts.Values) { 50 | foreach (var methodContext in typeContext.MethodContexts) { 51 | try { 52 | InjectMethodChecks(methodContext); 53 | } 54 | catch (Exception ex) { 55 | throw new RuntimeNullablesException($"Error injecting method '{methodContext.Method}' with null checks.", ex); 56 | } 57 | } 58 | } 59 | 60 | weavingContext.WriteInfo("Injection complete."); 61 | } 62 | 63 | public bool? GetConfigBooleanValue(string attributeName) 64 | { 65 | if (Config?.Attribute(attributeName)?.Value is string value) { 66 | try { 67 | return XmlConvert.ToBoolean(value); 68 | } 69 | catch { 70 | throw new RuntimeNullablesException($"Invalid value '{value}' for '{attributeName}' attribute."); 71 | } 72 | } 73 | 74 | return null; 75 | } 76 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.InternalSubClass.Release.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class private auto ansi beforefieldinit TestAssemblyNonPublic.InternalSubClass 2 | extends TestAssemblyNonPublic.PublicBaseClass 3 | implements TestAssemblyNonPublic.IPublicInterface, 4 | TestAssemblyNonPublic.IInternalInterface 5 | { 6 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 7 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 8 | .interfaceimpl type TestAssemblyNonPublic.IPublicInterface 9 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 10 | .interfaceimpl type TestAssemblyNonPublic.IInternalInterface 11 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 12 | .method public hidebysig virtual instance void 13 | AbstractPublicMethod(string 'value') cil managed 14 | { 15 | .maxstack 8 16 | IL_0000: ldarg.1 17 | IL_0001: brtrue.s IL_000d 18 | IL_0003: ldstr "value" 19 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 20 | IL_000d: ret 21 | } 22 | .method public hidebysig virtual instance void 23 | VirtualPublicMethod(string 'value') cil managed 24 | { 25 | .maxstack 8 26 | IL_0000: ldarg.1 27 | IL_0001: brtrue.s IL_000d 28 | IL_0003: ldstr "value" 29 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 30 | IL_000d: ret 31 | } 32 | .method assembly hidebysig strict virtual 33 | instance void AbstractInternalMethod(string 'value') cil managed 34 | { 35 | .maxstack 8 36 | IL_0000: ret 37 | } 38 | .method assembly hidebysig strict virtual 39 | instance void VirtualInternalMethod(string 'value') cil managed 40 | { 41 | .maxstack 8 42 | IL_0000: ret 43 | } 44 | .method private hidebysig newslot virtual final 45 | instance void TestAssemblyNonPublic.IPublicInterface.InterfacePublicMethod(string 'value') cil managed 46 | { 47 | .override TestAssemblyNonPublic.IPublicInterface::InterfacePublicMethod 48 | .maxstack 8 49 | IL_0000: ldarg.1 50 | IL_0001: brtrue.s IL_000d 51 | IL_0003: ldstr "value" 52 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 53 | IL_000d: ret 54 | } 55 | .method private hidebysig newslot virtual final 56 | instance void TestAssemblyNonPublic.IInternalInterface.InterfaceInternalMethod(string 'value') cil managed 57 | { 58 | .override TestAssemblyNonPublic.IInternalInterface::InterfaceInternalMethod 59 | .maxstack 8 60 | IL_0000: ret 61 | } 62 | .method public hidebysig specialname rtspecialname 63 | instance void .ctor() cil managed 64 | { 65 | .maxstack 8 66 | IL_0000: ldarg.0 67 | IL_0001: call instance void TestAssemblyNonPublic.PublicBaseClass::.ctor() 68 | IL_0006: ret 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.InternalSubClass.Release.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class private auto ansi beforefieldinit TestAssemblyNonPublic.InternalSubClass 2 | extends TestAssemblyNonPublic.PublicBaseClass 3 | implements TestAssemblyNonPublic.IPublicInterface, 4 | TestAssemblyNonPublic.IInternalInterface 5 | { 6 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 7 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 8 | .interfaceimpl type TestAssemblyNonPublic.IPublicInterface 9 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 10 | .interfaceimpl type TestAssemblyNonPublic.IInternalInterface 11 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 12 | .method public hidebysig virtual instance void 13 | AbstractPublicMethod(string 'value') cil managed 14 | { 15 | .maxstack 8 16 | IL_0000: ldarg.1 17 | IL_0001: brtrue.s IL_000d 18 | IL_0003: ldstr "value" 19 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 20 | IL_000d: ret 21 | } 22 | .method public hidebysig virtual instance void 23 | VirtualPublicMethod(string 'value') cil managed 24 | { 25 | .maxstack 8 26 | IL_0000: ldarg.1 27 | IL_0001: brtrue.s IL_000d 28 | IL_0003: ldstr "value" 29 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 30 | IL_000d: ret 31 | } 32 | .method assembly hidebysig strict virtual 33 | instance void AbstractInternalMethod(string 'value') cil managed 34 | { 35 | .maxstack 8 36 | IL_0000: ret 37 | } 38 | .method assembly hidebysig strict virtual 39 | instance void VirtualInternalMethod(string 'value') cil managed 40 | { 41 | .maxstack 8 42 | IL_0000: ret 43 | } 44 | .method private hidebysig newslot virtual final 45 | instance void TestAssemblyNonPublic.IPublicInterface.InterfacePublicMethod(string 'value') cil managed 46 | { 47 | .override TestAssemblyNonPublic.IPublicInterface::InterfacePublicMethod 48 | .maxstack 8 49 | IL_0000: ldarg.1 50 | IL_0001: brtrue.s IL_000d 51 | IL_0003: ldstr "value" 52 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 53 | IL_000d: ret 54 | } 55 | .method private hidebysig newslot virtual final 56 | instance void TestAssemblyNonPublic.IInternalInterface.InterfaceInternalMethod(string 'value') cil managed 57 | { 58 | .override TestAssemblyNonPublic.IInternalInterface::InterfaceInternalMethod 59 | .maxstack 8 60 | IL_0000: ret 61 | } 62 | .method public hidebysig specialname rtspecialname 63 | instance void .ctor() cil managed 64 | { 65 | .maxstack 8 66 | IL_0000: ldarg.0 67 | IL_0001: call instance void TestAssemblyNonPublic.PublicBaseClass::.ctor() 68 | IL_0006: ret 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/BclReferences.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom.Compiler; 3 | using System.Linq; 4 | using Mono.Cecil; 5 | 6 | namespace RuntimeNullables.Fody; 7 | 8 | internal class BclReferences 9 | { 10 | /// 11 | /// Gets a reference to . 12 | /// 13 | public MethodReference ArgumentNullExceptionConstructor { get; } 14 | 15 | /// 16 | /// Gets a reference to . 17 | /// 18 | public MethodReference NullReferenceExceptionConstructor { get; } 19 | 20 | /// 21 | /// Gets a reference to . 22 | /// 23 | public MethodReference GeneratedCodeAttributeConstructor { get; } 24 | 25 | public TypeReference VoidType { get; } 26 | 27 | public TypeReference ObjectType { get; } 28 | 29 | public TypeReference BoolType { get; } 30 | 31 | public TypeReference StringType { get; } 32 | 33 | public TypeReference ExceptionType { get; } 34 | 35 | public BclReferences(ModuleWeaver weaver) 36 | { 37 | VoidType = weaver.TypeSystem.VoidReference; 38 | ObjectType = weaver.TypeSystem.ObjectReference; 39 | BoolType = weaver.TypeSystem.BooleanReference; 40 | StringType = weaver.TypeSystem.StringReference; 41 | ExceptionType = GetBclType("System.Exception", weaver); 42 | 43 | ArgumentNullExceptionConstructor = GetBclConstructorWithStringParameters("System.ArgumentNullException", 1, weaver); 44 | NullReferenceExceptionConstructor = GetBclConstructorWithStringParameters("System.NullReferenceException", 1, weaver); 45 | GeneratedCodeAttributeConstructor = GetBclConstructorWithStringParameters("System.CodeDom.Compiler.GeneratedCodeAttribute", 2, weaver); 46 | } 47 | 48 | private MethodReference GetBclConstructorWithStringParameters(string typeName, int paramCount, ModuleWeaver weaver) 49 | { 50 | var typeDefinition = weaver.FindTypeDefinition(typeName) ?? throw new RuntimeNullablesException($"Unable to find required BCL type '{typeName}'."); 51 | 52 | var constructor = typeDefinition.Methods.FirstOrDefault( 53 | m => m.IsConstructor && m.Parameters.Count == paramCount && m.Parameters.All(p => p.ParameterType.FullName == StringType.FullName)); 54 | 55 | if (constructor == null) 56 | throw new RuntimeNullablesException($"Unable to find required BCL type constructor on '{typeName}'."); 57 | 58 | return weaver.ModuleDefinition.ImportReference(constructor); 59 | } 60 | 61 | private static TypeReference GetBclType(string typeName, ModuleWeaver weaver) 62 | { 63 | var type = weaver.FindTypeDefinition(typeName) ?? throw new RuntimeNullablesException($"Unable to find required BCL type '{typeName}'."); 64 | return weaver.ModuleDefinition.ImportReference(type); 65 | } 66 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.InternalSubClass.Debug.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class private auto ansi beforefieldinit TestAssemblyNonPublic.InternalSubClass 2 | extends TestAssemblyNonPublic.PublicBaseClass 3 | implements TestAssemblyNonPublic.IPublicInterface, 4 | TestAssemblyNonPublic.IInternalInterface 5 | { 6 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 7 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 8 | .interfaceimpl type TestAssemblyNonPublic.IPublicInterface 9 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 10 | .interfaceimpl type TestAssemblyNonPublic.IInternalInterface 11 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 12 | .method public hidebysig virtual instance void 13 | AbstractPublicMethod(string 'value') cil managed 14 | { 15 | .maxstack 8 16 | IL_0000: ldarg.1 17 | IL_0001: brtrue.s IL_000d 18 | IL_0003: ldstr "value" 19 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 20 | IL_000d: nop 21 | IL_000e: ret 22 | } 23 | .method public hidebysig virtual instance void 24 | VirtualPublicMethod(string 'value') cil managed 25 | { 26 | .maxstack 8 27 | IL_0000: ldarg.1 28 | IL_0001: brtrue.s IL_000d 29 | IL_0003: ldstr "value" 30 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 31 | IL_000d: nop 32 | IL_000e: ret 33 | } 34 | .method assembly hidebysig strict virtual 35 | instance void AbstractInternalMethod(string 'value') cil managed 36 | { 37 | .maxstack 8 38 | IL_0000: nop 39 | IL_0001: ret 40 | } 41 | .method assembly hidebysig strict virtual 42 | instance void VirtualInternalMethod(string 'value') cil managed 43 | { 44 | .maxstack 8 45 | IL_0000: nop 46 | IL_0001: ret 47 | } 48 | .method private hidebysig newslot virtual final 49 | instance void TestAssemblyNonPublic.IPublicInterface.InterfacePublicMethod(string 'value') cil managed 50 | { 51 | .override TestAssemblyNonPublic.IPublicInterface::InterfacePublicMethod 52 | .maxstack 8 53 | IL_0000: ldarg.1 54 | IL_0001: brtrue.s IL_000d 55 | IL_0003: ldstr "value" 56 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 57 | IL_000d: nop 58 | IL_000e: ret 59 | } 60 | .method private hidebysig newslot virtual final 61 | instance void TestAssemblyNonPublic.IInternalInterface.InterfaceInternalMethod(string 'value') cil managed 62 | { 63 | .override TestAssemblyNonPublic.IInternalInterface::InterfaceInternalMethod 64 | .maxstack 8 65 | IL_0000: nop 66 | IL_0001: ret 67 | } 68 | .method public hidebysig specialname rtspecialname 69 | instance void .ctor() cil managed 70 | { 71 | .maxstack 8 72 | IL_0000: ldarg.0 73 | IL_0001: call instance void TestAssemblyNonPublic.PublicBaseClass::.ctor() 74 | IL_0006: nop 75 | IL_0007: ret 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.InternalSubClass.Debug.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class private auto ansi beforefieldinit TestAssemblyNonPublic.InternalSubClass 2 | extends TestAssemblyNonPublic.PublicBaseClass 3 | implements TestAssemblyNonPublic.IPublicInterface, 4 | TestAssemblyNonPublic.IInternalInterface 5 | { 6 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 7 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 8 | .interfaceimpl type TestAssemblyNonPublic.IPublicInterface 9 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 10 | .interfaceimpl type TestAssemblyNonPublic.IInternalInterface 11 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 12 | .method public hidebysig virtual instance void 13 | AbstractPublicMethod(string 'value') cil managed 14 | { 15 | .maxstack 8 16 | IL_0000: ldarg.1 17 | IL_0001: brtrue.s IL_000d 18 | IL_0003: ldstr "value" 19 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 20 | IL_000d: nop 21 | IL_000e: ret 22 | } 23 | .method public hidebysig virtual instance void 24 | VirtualPublicMethod(string 'value') cil managed 25 | { 26 | .maxstack 8 27 | IL_0000: ldarg.1 28 | IL_0001: brtrue.s IL_000d 29 | IL_0003: ldstr "value" 30 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 31 | IL_000d: nop 32 | IL_000e: ret 33 | } 34 | .method assembly hidebysig strict virtual 35 | instance void AbstractInternalMethod(string 'value') cil managed 36 | { 37 | .maxstack 8 38 | IL_0000: nop 39 | IL_0001: ret 40 | } 41 | .method assembly hidebysig strict virtual 42 | instance void VirtualInternalMethod(string 'value') cil managed 43 | { 44 | .maxstack 8 45 | IL_0000: nop 46 | IL_0001: ret 47 | } 48 | .method private hidebysig newslot virtual final 49 | instance void TestAssemblyNonPublic.IPublicInterface.InterfacePublicMethod(string 'value') cil managed 50 | { 51 | .override TestAssemblyNonPublic.IPublicInterface::InterfacePublicMethod 52 | .maxstack 8 53 | IL_0000: ldarg.1 54 | IL_0001: brtrue.s IL_000d 55 | IL_0003: ldstr "value" 56 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 57 | IL_000d: nop 58 | IL_000e: ret 59 | } 60 | .method private hidebysig newslot virtual final 61 | instance void TestAssemblyNonPublic.IInternalInterface.InterfaceInternalMethod(string 'value') cil managed 62 | { 63 | .override TestAssemblyNonPublic.IInternalInterface::InterfaceInternalMethod 64 | .maxstack 8 65 | IL_0000: nop 66 | IL_0001: ret 67 | } 68 | .method public hidebysig specialname rtspecialname 69 | instance void .ctor() cil managed 70 | { 71 | .maxstack 8 72 | IL_0000: ldarg.0 73 | IL_0001: call instance void TestAssemblyNonPublic.PublicBaseClass::.ctor() 74 | IL_0006: nop 75 | IL_0007: ret 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Source/TestAssembly/TaskResults.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using RuntimeNullables; 3 | 4 | #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task 5 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 6 | 7 | namespace TestAssembly; 8 | 9 | public static class TaskResults 10 | { 11 | public static Task GoodReference() 12 | { 13 | return Task.FromResult(string.Empty); 14 | } 15 | 16 | public static Task BadReference() 17 | { 18 | return Task.FromResult(null!); 19 | } 20 | 21 | public static Task GoodGeneric() where T : notnull, new() 22 | { 23 | return Task.FromResult(new T()); 24 | } 25 | 26 | public static Task BadGeneric() where T : notnull 27 | { 28 | return Task.FromResult(default!); 29 | } 30 | 31 | public static async Task GoodReferenceAsync() 32 | { 33 | await Task.Delay(10); 34 | return string.Empty; 35 | } 36 | 37 | public static async Task BadReferenceAsync() 38 | { 39 | await Task.Delay(10); 40 | return null!; 41 | } 42 | 43 | public static async Task GoodReferenceAsyncWithoutAwait() 44 | { 45 | return string.Empty; 46 | } 47 | 48 | public static async Task BadReferenceAsyncWithoutAwait() 49 | { 50 | return null!; 51 | } 52 | 53 | public static ValueTask GoodReferenceValue() 54 | { 55 | return new ValueTask(string.Empty); 56 | } 57 | 58 | public static ValueTask BadReferenceValue() 59 | { 60 | return new ValueTask((string)null!); 61 | } 62 | 63 | public static async ValueTask GoodReferenceValueAsync() 64 | { 65 | await Task.Delay(10); 66 | return string.Empty; 67 | } 68 | 69 | public static async ValueTask BadReferenceValueAsync() 70 | { 71 | await Task.Delay(100); 72 | return null!; 73 | } 74 | 75 | public static async ValueTask GoodReferenceValueAsyncWithoutAwait() 76 | { 77 | return string.Empty; 78 | } 79 | 80 | public static async ValueTask BadReferenceAsyncValueWithoutAwait() 81 | { 82 | return null!; 83 | } 84 | 85 | public static async Task GoodGenericAsync() where T : notnull, new() 86 | { 87 | await Task.Delay(10); 88 | return new T(); 89 | } 90 | 91 | public static async Task BadGenericAsync() where T : notnull 92 | { 93 | await Task.Delay(10); 94 | return default!; 95 | } 96 | 97 | public static Task DefaultValueType() 98 | { 99 | return Task.FromResult(false); 100 | } 101 | 102 | public static Task DefaultNullableValueType() 103 | { 104 | return Task.FromResult((bool?)null); 105 | } 106 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Contexts/NullableContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mono.Cecil; 3 | using RuntimeNullables.Fody.Extensions; 4 | 5 | namespace RuntimeNullables.Fody.Contexts; 6 | 7 | internal abstract class NullableContext 8 | { 9 | private readonly bool _isNullable; 10 | private readonly bool _nullChecksEnabled; 11 | 12 | private bool _buildCalled; 13 | 14 | /// 15 | /// Initializes a new instance of the class. This constructor should only be used for root contexts with no parent 16 | /// contexts. 17 | /// 18 | internal NullableContext(ICustomAttributeProvider attributeProvider, WeavingContext weavingContext) 19 | { 20 | WeavingContext = weavingContext; 21 | 22 | _isNullable = true; 23 | _nullChecksEnabled = true; 24 | 25 | ProcessAttributes(attributeProvider, ref _isNullable, ref _nullChecksEnabled); 26 | } 27 | 28 | internal NullableContext(ICustomAttributeProvider attributeProvider, NullableContext parentContext) 29 | { 30 | WeavingContext = parentContext.WeavingContext; 31 | 32 | _isNullable = parentContext._isNullable; 33 | _nullChecksEnabled = parentContext._nullChecksEnabled; 34 | 35 | ProcessAttributes(attributeProvider, ref _isNullable, ref _nullChecksEnabled); 36 | } 37 | 38 | /// 39 | /// Gets the current weaving context. 40 | /// 41 | public WeavingContext WeavingContext { get; } 42 | 43 | /// 44 | /// Gets a value indicating whether the current context is nullable by default. 45 | /// 46 | public bool IsNullable => _isNullable; 47 | 48 | /// 49 | /// Gets a value indicating whether the current context has null checks enabled. 50 | /// 51 | public bool NullChecksEnabled => _nullChecksEnabled; 52 | 53 | /// 54 | /// Builds the context data. 55 | /// 56 | public virtual void Build() 57 | { 58 | if (_buildCalled) 59 | throw new InvalidOperationException("Build method has already been called on this context."); 60 | 61 | _buildCalled = true; 62 | } 63 | 64 | /// 65 | /// Determines the nullability of an item that belongs to this type. 66 | /// 67 | protected bool IsContextItemNullable(ICustomAttributeProvider item) => IsContextInnerItemNullable(item, -1); 68 | 69 | protected bool IsContextInnerItemNullable(ICustomAttributeProvider item, int innerOrdinal) 70 | { 71 | var nullable = item.GetNullableAttributeValue(innerOrdinal + 1, WeavingContext); 72 | return nullable == null ? IsNullable : nullable.GetValueOrDefault().ToIsNullable(WeavingContext); 73 | } 74 | 75 | private void ProcessAttributes(ICustomAttributeProvider item, ref bool nullable, ref bool nullChecksEnabled) 76 | { 77 | if (item.GetNullChecksAttributeValue(WeavingContext, true) is bool nullChecksValue) 78 | nullChecksEnabled = nullChecksValue; 79 | 80 | if (item.GetNullableContextAttributeValue(WeavingContext) is NullableValue nullableValue) 81 | nullable = nullableValue.ToIsNullable(WeavingContext); 82 | } 83 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Contexts/MethodContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Mono.Cecil; 5 | using RuntimeNullables.Fody.Extensions; 6 | 7 | namespace RuntimeNullables.Fody.Contexts; 8 | 9 | internal class MethodContext : NullableContext 10 | { 11 | private List? _genericParameters; 12 | 13 | public MethodDefinition Method { get; } 14 | 15 | public TypeContext TypeContext { get; } 16 | 17 | public ICollection GenericParameters => _genericParameters ?? 18 | throw new InvalidOperationException("Method does not have generic parameters."); 19 | 20 | public MethodContext(MethodDefinition method, TypeContext typeContext) : base(method, typeContext) 21 | { 22 | Method = method; 23 | TypeContext = typeContext; 24 | } 25 | 26 | public MethodContext(MethodDefinition method, PropertyContext propertyContext) : base(method, propertyContext) 27 | { 28 | Method = method; 29 | TypeContext = propertyContext.TypeContext; 30 | } 31 | 32 | public override void Build() 33 | { 34 | base.Build(); 35 | 36 | if (Method.HasGenericParameters) 37 | _genericParameters = Method.GenericParameters.Select(p => new GenericParameterInfo(p, IsContextItemNullable(p))).ToList(); 38 | } 39 | 40 | public bool IsParameterNullable(ParameterDefinition parameter) 41 | { 42 | if (IsContextItemNullable(parameter)) 43 | return true; 44 | 45 | return parameter.ParameterType.GetElementType() is GenericParameter genericParameter && GetGenericParameterIsNullable(genericParameter); 46 | } 47 | 48 | public bool IsReturnValueNullable() 49 | { 50 | if (IsContextItemNullable(Method.MethodReturnType)) 51 | return true; 52 | 53 | return Method.ReturnType is GenericParameter genericParameter && GetGenericParameterIsNullable(genericParameter); 54 | } 55 | 56 | public bool IsReturnValueGenericArgumentNullableOrValueType() 57 | { 58 | if (!(Method.ReturnType is GenericInstanceType genericReturnType)) 59 | throw new InvalidOperationException("Return type is not a generic instance type."); 60 | 61 | if (!genericReturnType.GenericArguments[0].IsReferenceType()) 62 | return true; 63 | 64 | if (IsContextInnerItemNullable(Method.MethodReturnType, 0)) 65 | return true; 66 | 67 | return genericReturnType.GenericArguments[0] is GenericParameter genericParameter && GetGenericParameterIsNullable(genericParameter); 68 | } 69 | 70 | /// 71 | /// Walks the parent hierarchy of contexts to find the cached generic parameter nullability. 72 | /// 73 | public bool GetGenericParameterIsNullable(GenericParameter parameter) 74 | { 75 | if (parameter.Owner != Method) 76 | return TypeContext.GetGenericParameterIsNullable(parameter); 77 | 78 | var paramInfo = GenericParameters.FirstOrDefault(p => p.Parameter == parameter); 79 | 80 | if (paramInfo.Parameter == null) { 81 | WeavingContext.WriteError($"Could not resolve generic parameter '{parameter}': parameter not found."); 82 | return true; 83 | } 84 | 85 | return paramInfo.Nullable; 86 | } 87 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Contexts/TypeContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Mono.Cecil; 5 | 6 | namespace RuntimeNullables.Fody.Contexts; 7 | 8 | internal sealed class TypeContext : NullableContext 9 | { 10 | private readonly List _methodContexts = new(); 11 | private List? _genericParameters; 12 | 13 | public TypeDefinition Type { get; } 14 | 15 | public ModuleContext ModuleContext { get; } 16 | 17 | public TypeContext? DeclaringTypeContext { get; } 18 | 19 | public ICollection MethodContexts => _methodContexts; 20 | 21 | public ICollection GenericParameters => _genericParameters ?? 22 | throw new InvalidOperationException("Type does not have generic parameters."); 23 | 24 | public TypeContext(TypeDefinition type, ModuleContext moduleContext) : base(type, moduleContext) 25 | { 26 | Type = type; 27 | ModuleContext = moduleContext; 28 | } 29 | 30 | public TypeContext(TypeDefinition type, TypeContext declaringTypeContext) : base(type, declaringTypeContext) 31 | { 32 | Type = type; 33 | DeclaringTypeContext = declaringTypeContext; 34 | ModuleContext = declaringTypeContext.ModuleContext; 35 | } 36 | 37 | public override void Build() 38 | { 39 | base.Build(); 40 | 41 | if (Type.HasGenericParameters) 42 | _genericParameters = Type.GenericParameters.Select(p => new GenericParameterInfo(p, IsContextItemNullable(p))).ToList(); 43 | 44 | foreach (var method in Type.Methods.Where(m => m.HasBody && !m.IsGetter && !m.IsSetter && !m.HasAnyGeneratedAttribute())) { 45 | var methodContext = new MethodContext(method, this); 46 | methodContext.Build(); 47 | _methodContexts.Add(methodContext); 48 | } 49 | 50 | foreach (var property in Type.Properties.Where(p => (p.GetMethod ?? p.SetMethod).HasBody && !p.HasAnyGeneratedAttribute())) { 51 | var propertyContext = new PropertyContext(property, this); 52 | propertyContext.Build(); 53 | 54 | if (propertyContext.GetMethodContext != null) 55 | _methodContexts.Add(propertyContext.GetMethodContext); 56 | 57 | if (propertyContext.SetMethodContext != null) 58 | _methodContexts.Add(propertyContext.SetMethodContext); 59 | } 60 | } 61 | 62 | /// 63 | /// Walks the parent hierarchy of contexts to find the generic parameter and get its nullability. 64 | /// 65 | public bool GetGenericParameterIsNullable(GenericParameter parameter) 66 | { 67 | if (parameter.Owner != Type) { 68 | if (DeclaringTypeContext == null) { 69 | WeavingContext.WriteError($"Could not resolve generic parameter '{parameter}': owner '{parameter.Owner}' not found."); 70 | return true; 71 | } 72 | 73 | return DeclaringTypeContext.GetGenericParameterIsNullable(parameter); 74 | } 75 | 76 | var paramInfo = GenericParameters.FirstOrDefault(p => p.Parameter == parameter); 77 | 78 | if (paramInfo.Parameter == null) { 79 | WeavingContext.WriteError($"Could not resolve generic parameter '{parameter}': parameter not found."); 80 | return true; 81 | } 82 | 83 | return paramInfo.Nullable; 84 | } 85 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/TaskResultTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using TestAssembly; 5 | 6 | #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task 7 | 8 | namespace RuntimeNullables.Fody.Tests; 9 | 10 | [TestClass] 11 | public class TaskResultTests 12 | { 13 | [TestMethod] 14 | public void GoodReference() 15 | { 16 | TaskResults.GoodReference(); 17 | } 18 | 19 | [TestMethod] 20 | public void BadReference() 21 | { 22 | Assert.ThrowsException(() => TaskResults.BadReference()); 23 | } 24 | 25 | [TestMethod] 26 | public void GoodGeneric() 27 | { 28 | TaskResults.GoodGeneric(); 29 | } 30 | 31 | [TestMethod] 32 | public void BadGeneric() 33 | { 34 | Assert.ThrowsException(() => TaskResults.BadGeneric()); 35 | } 36 | 37 | [TestMethod] 38 | public Task GoodReferenceAsync() 39 | { 40 | return TaskResults.GoodReferenceAsync(); 41 | } 42 | 43 | [TestMethod] 44 | public Task BadReferenceAsync() 45 | { 46 | return Assert.ThrowsExceptionAsync(() => TaskResults.BadReferenceAsync()); 47 | } 48 | 49 | [TestMethod] 50 | public Task GoodReferenceAsyncWithoutAwait() 51 | { 52 | return TaskResults.GoodReferenceAsyncWithoutAwait(); 53 | } 54 | 55 | [TestMethod] 56 | public Task BadReferenceAsyncWithoutAwait() 57 | { 58 | return Assert.ThrowsExceptionAsync(() => TaskResults.BadReferenceAsyncWithoutAwait()); 59 | } 60 | 61 | [TestMethod] 62 | public Task GoodReferenceValue() 63 | { 64 | return TaskResults.GoodReferenceValue().AsTask(); 65 | } 66 | 67 | [TestMethod] 68 | public Task BadReferenceValue() 69 | { 70 | return Assert.ThrowsExceptionAsync(() => TaskResults.BadReferenceValue().AsTask()); 71 | } 72 | 73 | [TestMethod] 74 | public Task GoodReferenceValueAsync() 75 | { 76 | return TaskResults.GoodReferenceValueAsync().AsTask(); 77 | } 78 | 79 | [TestMethod] 80 | public Task BadReferenceValueAsync() 81 | { 82 | return Assert.ThrowsExceptionAsync(() => TaskResults.BadReferenceValueAsync().AsTask()); 83 | } 84 | 85 | [TestMethod] 86 | public Task GoodReferenceValueAsyncWithoutAwait() 87 | { 88 | return TaskResults.GoodReferenceValueAsyncWithoutAwait().AsTask(); 89 | } 90 | 91 | [TestMethod] 92 | public Task BadReferenceAsyncValueWithoutAwait() 93 | { 94 | return Assert.ThrowsExceptionAsync(() => TaskResults.BadReferenceAsyncValueWithoutAwait().AsTask()); 95 | } 96 | 97 | [TestMethod] 98 | public Task GoodGenericNotNullConstraintAsync() 99 | { 100 | return TaskResults.GoodGenericAsync(); 101 | } 102 | 103 | [TestMethod] 104 | public Task BadGenericNotNullConstraintAsync() 105 | { 106 | return Assert.ThrowsExceptionAsync(() => TaskResults.BadGenericAsync()); 107 | } 108 | 109 | [TestMethod] 110 | public async Task DefaultValueType() 111 | { 112 | Assert.AreEqual(false, await TaskResults.DefaultValueType()); 113 | } 114 | 115 | [TestMethod] 116 | public async Task DefaultNullableValueType() 117 | { 118 | Assert.AreEqual(null, await TaskResults.DefaultNullableValueType()); 119 | } 120 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.InputParameters.Release.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.InputParameters 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig static void Reference(string 'value') cil managed 7 | { 8 | .maxstack 8 9 | IL_0000: ldarg.0 10 | IL_0001: brtrue.s IL_000d 11 | IL_0003: ldstr "value" 12 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 13 | IL_000d: ret 14 | } 15 | .method public hidebysig static void NoNullChecksReference(string 'value') cil managed 16 | { 17 | .maxstack 8 18 | IL_0000: ret 19 | } 20 | .method public hidebysig static void NullableReference(string 'value') cil managed 21 | { 22 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 23 | .maxstack 8 24 | IL_0000: ret 25 | } 26 | .method public hidebysig static void ValueArray(uint8[] 'value') cil managed 27 | { 28 | .maxstack 8 29 | IL_0000: ldarg.0 30 | IL_0001: brtrue.s IL_000d 31 | IL_0003: ldstr "value" 32 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 33 | IL_000d: ret 34 | } 35 | .method public hidebysig static void GenericUnconstrained(!!T 'value') cil managed 36 | { 37 | .param type T 38 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 39 | .maxstack 8 40 | IL_0000: ret 41 | } 42 | .method public hidebysig static void GenericUnconstrainedIn([in] !!T& 'value') cil managed 43 | { 44 | .param type T 45 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 46 | .param [1] 47 | .maxstack 8 48 | IL_0000: ret 49 | } 50 | .method public hidebysig static void GenericNotNullConstraint(!!T 'value') cil managed 51 | { 52 | .maxstack 8 53 | IL_0000: ldarg.0 54 | IL_0001: box !!T 55 | IL_0006: brtrue.s IL_0012 56 | IL_0008: ldstr "value" 57 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 58 | IL_0012: ret 59 | } 60 | .method public hidebysig static void GenericDisallowNull(!!T 'value') cil managed 61 | { 62 | .param type T 63 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 64 | .param [1] 65 | .maxstack 8 66 | IL_0000: ldarg.0 67 | IL_0001: box !!T 68 | IL_0006: brtrue.s IL_0012 69 | IL_0008: ldstr "value" 70 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 71 | IL_0012: ret 72 | } 73 | .method public hidebysig static void GenericClassConstraint(!!T 'value') cil managed 74 | { 75 | .maxstack 8 76 | IL_0000: ldarg.0 77 | IL_0001: box !!T 78 | IL_0006: brtrue.s IL_0012 79 | IL_0008: ldstr "value" 80 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 81 | IL_0012: ret 82 | } 83 | .method public hidebysig static void GenericClassConstraintWithAllowNull(!!T 'value') cil managed 84 | { 85 | .param [1] 86 | .maxstack 8 87 | IL_0000: ret 88 | } 89 | .method public hidebysig static void GenericClassConstraintWithNullable(!!T 'value') cil managed 90 | { 91 | .param [1] 92 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 93 | .maxstack 8 94 | IL_0000: ret 95 | } 96 | .method public hidebysig static void GenericNullableClassConstraint(!!T 'value') cil managed 97 | { 98 | .param type T 99 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 100 | .maxstack 8 101 | IL_0000: ret 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.InputParameters.Release.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.InputParameters 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig static void Reference(string 'value') cil managed 7 | { 8 | .maxstack 8 9 | IL_0000: ldarg.0 10 | IL_0001: brtrue.s IL_000d 11 | IL_0003: ldstr "value" 12 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 13 | IL_000d: ret 14 | } 15 | .method public hidebysig static void NoNullChecksReference(string 'value') cil managed 16 | { 17 | .maxstack 8 18 | IL_0000: ret 19 | } 20 | .method public hidebysig static void NullableReference(string 'value') cil managed 21 | { 22 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 23 | .maxstack 8 24 | IL_0000: ret 25 | } 26 | .method public hidebysig static void ValueArray(uint8[] 'value') cil managed 27 | { 28 | .maxstack 8 29 | IL_0000: ldarg.0 30 | IL_0001: brtrue.s IL_000d 31 | IL_0003: ldstr "value" 32 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 33 | IL_000d: ret 34 | } 35 | .method public hidebysig static void GenericUnconstrained(!!T 'value') cil managed 36 | { 37 | .param type T 38 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 39 | .maxstack 8 40 | IL_0000: ret 41 | } 42 | .method public hidebysig static void GenericUnconstrainedIn([in] !!T& 'value') cil managed 43 | { 44 | .param type T 45 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 46 | .param [1] 47 | .maxstack 8 48 | IL_0000: ret 49 | } 50 | .method public hidebysig static void GenericNotNullConstraint(!!T 'value') cil managed 51 | { 52 | .maxstack 8 53 | IL_0000: ldarg.0 54 | IL_0001: box !!T 55 | IL_0006: brtrue.s IL_0012 56 | IL_0008: ldstr "value" 57 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 58 | IL_0012: ret 59 | } 60 | .method public hidebysig static void GenericDisallowNull(!!T 'value') cil managed 61 | { 62 | .param type T 63 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 64 | .param [1] 65 | .maxstack 8 66 | IL_0000: ldarg.0 67 | IL_0001: box !!T 68 | IL_0006: brtrue.s IL_0012 69 | IL_0008: ldstr "value" 70 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 71 | IL_0012: ret 72 | } 73 | .method public hidebysig static void GenericClassConstraint(!!T 'value') cil managed 74 | { 75 | .maxstack 8 76 | IL_0000: ldarg.0 77 | IL_0001: box !!T 78 | IL_0006: brtrue.s IL_0012 79 | IL_0008: ldstr "value" 80 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 81 | IL_0012: ret 82 | } 83 | .method public hidebysig static void GenericClassConstraintWithAllowNull(!!T 'value') cil managed 84 | { 85 | .param [1] 86 | .maxstack 8 87 | IL_0000: ret 88 | } 89 | .method public hidebysig static void GenericClassConstraintWithNullable(!!T 'value') cil managed 90 | { 91 | .param [1] 92 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 93 | .maxstack 8 94 | IL_0000: ret 95 | } 96 | .method public hidebysig static void GenericNullableClassConstraint(!!T 'value') cil managed 97 | { 98 | .param type T 99 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 100 | .maxstack 8 101 | IL_0000: ret 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody/Extensions/AttributeProviderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom.Compiler; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Runtime.CompilerServices; 6 | using Mono.Cecil; 7 | using RuntimeNullables.Fody.Contexts; 8 | 9 | namespace RuntimeNullables.Fody; 10 | 11 | internal static class AttributeProviderExtensions 12 | { 13 | public static bool HasNotNullAttribute(this ICustomAttributeProvider item) => HasAttribute(item, "System.Diagnostics.CodeAnalysis.NotNullAttribute"); 14 | 15 | public static bool HasAnyMaybeNullAttribute(this ICustomAttributeProvider item) 16 | { 17 | return item.CustomAttributes.Any(a => a.AttributeType.FullName.StartsWith("System.Diagnostics.CodeAnalysis.MaybeNull", StringComparison.Ordinal)); 18 | } 19 | 20 | /// 21 | /// Gets a value indicating whether the item has or applied to it. 22 | /// 23 | public static bool HasAnyGeneratedAttribute(this ICustomAttributeProvider item) 24 | { 25 | return item.CustomAttributes.Select(a => a.AttributeType.FullName).Any( 26 | n => n is "System.CodeDom.Compiler.GeneratedCodeAttribute" or "System.Runtime.CompilerServices.CompilerGeneratedAttribute"); 27 | } 28 | 29 | public static bool? GetNullChecksAttributeValue(this ICustomAttributeProvider item, WeavingContext weavingContext, bool removeAttribute) 30 | { 31 | return GetConstructorArgValue(item, "RuntimeNullables.NullChecksAttribute", weavingContext, removeAttribute); 32 | } 33 | 34 | public static NullableValue? GetNullableAttributeValue(this ICustomAttributeProvider item, int itemOrdinal, WeavingContext weavingContext) 35 | { 36 | object argValue = GetConstructorArgValue(item, "System.Runtime.CompilerServices.NullableAttribute", weavingContext); 37 | 38 | if (argValue == null) 39 | return null; 40 | 41 | if (argValue is CustomAttributeArgument[] arrayValue) 42 | argValue = arrayValue[Math.Min(arrayValue.Length - 1, itemOrdinal)].Value; 43 | 44 | if (argValue is byte value) 45 | return (NullableValue)value; 46 | 47 | weavingContext.WriteError("Unexpected nullable attribute constructor argument type."); 48 | return null; 49 | } 50 | 51 | public static NullableValue? GetNullableContextAttributeValue(this ICustomAttributeProvider item, WeavingContext weavingContext) 52 | { 53 | return (NullableValue?)GetConstructorArgValue(item, "System.Runtime.CompilerServices.NullableContextAttribute", weavingContext); 54 | } 55 | 56 | public static bool HasAttribute(this ICustomAttributeProvider item, string attributeFullName) 57 | { 58 | return item.CustomAttributes.Any(a => a.AttributeType.FullName == attributeFullName); 59 | } 60 | 61 | [return: MaybeNull] 62 | public static T GetConstructorArgValue(this ICustomAttributeProvider item, string attributeFullName, WeavingContext weavingContext, bool removeAttribute = false) 63 | { 64 | var attribute = item.CustomAttributes.SingleOrDefault(a => a.AttributeType.FullName == attributeFullName); 65 | 66 | if (attribute == null) 67 | return default; 68 | 69 | if (removeAttribute) 70 | item.CustomAttributes.Remove(attribute); 71 | 72 | if (attribute.ConstructorArguments.Count is var argCount && argCount != 1) { 73 | weavingContext.WriteError($"Unexpected '{attributeFullName}' constructor argument count."); 74 | return default; 75 | } 76 | 77 | object value = attribute.ConstructorArguments[0].Value; 78 | 79 | try { 80 | return (T)value; 81 | } 82 | catch (InvalidCastException) { 83 | weavingContext.WriteError($"Unexpected '{attributeFullName}' constructor argument type: '{value.GetType()}'."); 84 | return default; 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.InputParameters.Debug.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.InputParameters 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig static void Reference(string 'value') cil managed 7 | { 8 | .maxstack 8 9 | IL_0000: ldarg.0 10 | IL_0001: brtrue.s IL_000d 11 | IL_0003: ldstr "value" 12 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 13 | IL_000d: nop 14 | IL_000e: ret 15 | } 16 | .method public hidebysig static void NoNullChecksReference(string 'value') cil managed 17 | { 18 | .maxstack 8 19 | IL_0000: nop 20 | IL_0001: ret 21 | } 22 | .method public hidebysig static void NullableReference(string 'value') cil managed 23 | { 24 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 25 | .maxstack 8 26 | IL_0000: nop 27 | IL_0001: ret 28 | } 29 | .method public hidebysig static void ValueArray(uint8[] 'value') cil managed 30 | { 31 | .maxstack 8 32 | IL_0000: ldarg.0 33 | IL_0001: brtrue.s IL_000d 34 | IL_0003: ldstr "value" 35 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 36 | IL_000d: nop 37 | IL_000e: ret 38 | } 39 | .method public hidebysig static void GenericUnconstrained(!!T 'value') cil managed 40 | { 41 | .param type T 42 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 43 | .maxstack 8 44 | IL_0000: nop 45 | IL_0001: ret 46 | } 47 | .method public hidebysig static void GenericUnconstrainedIn([in] !!T& 'value') cil managed 48 | { 49 | .param type T 50 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 51 | .param [1] 52 | .maxstack 8 53 | IL_0000: nop 54 | IL_0001: ret 55 | } 56 | .method public hidebysig static void GenericNotNullConstraint(!!T 'value') cil managed 57 | { 58 | .maxstack 8 59 | IL_0000: ldarg.0 60 | IL_0001: box !!T 61 | IL_0006: brtrue.s IL_0012 62 | IL_0008: ldstr "value" 63 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 64 | IL_0012: nop 65 | IL_0013: ret 66 | } 67 | .method public hidebysig static void GenericDisallowNull(!!T 'value') cil managed 68 | { 69 | .param type T 70 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 71 | .param [1] 72 | .maxstack 8 73 | IL_0000: ldarg.0 74 | IL_0001: box !!T 75 | IL_0006: brtrue.s IL_0012 76 | IL_0008: ldstr "value" 77 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 78 | IL_0012: nop 79 | IL_0013: ret 80 | } 81 | .method public hidebysig static void GenericClassConstraint(!!T 'value') cil managed 82 | { 83 | .maxstack 8 84 | IL_0000: ldarg.0 85 | IL_0001: box !!T 86 | IL_0006: brtrue.s IL_0012 87 | IL_0008: ldstr "value" 88 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 89 | IL_0012: nop 90 | IL_0013: ret 91 | } 92 | .method public hidebysig static void GenericClassConstraintWithAllowNull(!!T 'value') cil managed 93 | { 94 | .param [1] 95 | .maxstack 8 96 | IL_0000: nop 97 | IL_0001: ret 98 | } 99 | .method public hidebysig static void GenericClassConstraintWithNullable(!!T 'value') cil managed 100 | { 101 | .param [1] 102 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 103 | .maxstack 8 104 | IL_0000: nop 105 | IL_0001: ret 106 | } 107 | .method public hidebysig static void GenericNullableClassConstraint(!!T 'value') cil managed 108 | { 109 | .param type T 110 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 111 | .maxstack 8 112 | IL_0000: nop 113 | IL_0001: ret 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.InputParameters.Debug.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.InputParameters 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig static void Reference(string 'value') cil managed 7 | { 8 | .maxstack 8 9 | IL_0000: ldarg.0 10 | IL_0001: brtrue.s IL_000d 11 | IL_0003: ldstr "value" 12 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 13 | IL_000d: nop 14 | IL_000e: ret 15 | } 16 | .method public hidebysig static void NoNullChecksReference(string 'value') cil managed 17 | { 18 | .maxstack 8 19 | IL_0000: nop 20 | IL_0001: ret 21 | } 22 | .method public hidebysig static void NullableReference(string 'value') cil managed 23 | { 24 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 25 | .maxstack 8 26 | IL_0000: nop 27 | IL_0001: ret 28 | } 29 | .method public hidebysig static void ValueArray(uint8[] 'value') cil managed 30 | { 31 | .maxstack 8 32 | IL_0000: ldarg.0 33 | IL_0001: brtrue.s IL_000d 34 | IL_0003: ldstr "value" 35 | IL_0008: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 36 | IL_000d: nop 37 | IL_000e: ret 38 | } 39 | .method public hidebysig static void GenericUnconstrained(!!T 'value') cil managed 40 | { 41 | .param type T 42 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 43 | .maxstack 8 44 | IL_0000: nop 45 | IL_0001: ret 46 | } 47 | .method public hidebysig static void GenericUnconstrainedIn([in] !!T& 'value') cil managed 48 | { 49 | .param type T 50 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 51 | .param [1] 52 | .maxstack 8 53 | IL_0000: nop 54 | IL_0001: ret 55 | } 56 | .method public hidebysig static void GenericNotNullConstraint(!!T 'value') cil managed 57 | { 58 | .maxstack 8 59 | IL_0000: ldarg.0 60 | IL_0001: box !!T 61 | IL_0006: brtrue.s IL_0012 62 | IL_0008: ldstr "value" 63 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 64 | IL_0012: nop 65 | IL_0013: ret 66 | } 67 | .method public hidebysig static void GenericDisallowNull(!!T 'value') cil managed 68 | { 69 | .param type T 70 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 71 | .param [1] 72 | .maxstack 8 73 | IL_0000: ldarg.0 74 | IL_0001: box !!T 75 | IL_0006: brtrue.s IL_0012 76 | IL_0008: ldstr "value" 77 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 78 | IL_0012: nop 79 | IL_0013: ret 80 | } 81 | .method public hidebysig static void GenericClassConstraint(!!T 'value') cil managed 82 | { 83 | .maxstack 8 84 | IL_0000: ldarg.0 85 | IL_0001: box !!T 86 | IL_0006: brtrue.s IL_0012 87 | IL_0008: ldstr "value" 88 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowArgumentNull(string) 89 | IL_0012: nop 90 | IL_0013: ret 91 | } 92 | .method public hidebysig static void GenericClassConstraintWithAllowNull(!!T 'value') cil managed 93 | { 94 | .param [1] 95 | .maxstack 8 96 | IL_0000: nop 97 | IL_0001: ret 98 | } 99 | .method public hidebysig static void GenericClassConstraintWithNullable(!!T 'value') cil managed 100 | { 101 | .param [1] 102 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 103 | .maxstack 8 104 | IL_0000: nop 105 | IL_0001: ret 106 | } 107 | .method public hidebysig static void GenericNullableClassConstraint(!!T 'value') cil managed 108 | { 109 | .param type T 110 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 111 | .maxstack 8 112 | IL_0000: nop 113 | IL_0001: ret 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Fody; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using TestAssembly; 6 | using TestAssemblyNonPublic; 7 | using TestAssemblyPointers; 8 | using TestAssemblyThrowHelpers; 9 | using VerifyMSTest; 10 | using VerifyTests; 11 | 12 | namespace RuntimeNullables.Fody.Tests; 13 | 14 | [TestClass] 15 | public class VerifyILTests : VerifyBase 16 | { 17 | private readonly VerifySettings _verifySettings; 18 | 19 | public VerifyILTests() 20 | { 21 | _verifySettings = new VerifySettings(); 22 | _verifySettings.UniqueForRuntimeAndVersion(); 23 | _verifySettings.UniqueForAssemblyConfiguration(); 24 | 25 | // _verifySettings.AutoVerify(); 26 | } 27 | 28 | // TestAssembly: 29 | 30 | [TestMethod] 31 | public Task InjectedThrowHelpers() 32 | { 33 | return Verify(Decompile(typeof(Combo<>).Assembly.GetType("RuntimeNullables.ThrowHelpers")!), _verifySettings); 34 | } 35 | 36 | [TestMethod] 37 | public Task AsyncEnumerables() 38 | { 39 | return Verify(Decompile(typeof(AsyncEnumerables)), _verifySettings); 40 | } 41 | 42 | [TestMethod] 43 | public Task AsyncEnumerators() 44 | { 45 | return Verify(Decompile(typeof(AsyncEnumerators)), _verifySettings); 46 | } 47 | 48 | [TestMethod] 49 | public Task Combo() 50 | { 51 | return Verify(Decompile(typeof(Combo<>)), _verifySettings); 52 | } 53 | 54 | [TestMethod] 55 | public Task Enumerables() 56 | { 57 | return Verify(Decompile(typeof(Enumerables)), _verifySettings); 58 | } 59 | 60 | [TestMethod] 61 | public Task Enumerators() 62 | { 63 | return Verify(Decompile(typeof(Enumerators)), _verifySettings); 64 | } 65 | 66 | [TestMethod] 67 | public Task GeneratedCode() 68 | { 69 | return Verify(Decompile(typeof(GeneratedCode)), _verifySettings); 70 | } 71 | 72 | [TestMethod] 73 | public Task InputParameters() 74 | { 75 | return Verify(Decompile(typeof(InputParameters)), _verifySettings); 76 | } 77 | 78 | [TestMethod] 79 | public Task NestedContainer() 80 | { 81 | return Verify(Decompile(typeof(NestedContainer)), _verifySettings); 82 | } 83 | 84 | [TestMethod] 85 | public Task NullableDisabled() 86 | { 87 | return Verify(Decompile(typeof(NullableDisabled)), _verifySettings); 88 | } 89 | 90 | [TestMethod] 91 | public Task OutputParameters() 92 | { 93 | return Verify(Decompile(typeof(OutputParameters)), _verifySettings); 94 | } 95 | 96 | [TestMethod] 97 | public Task Properties() 98 | { 99 | return Verify(Decompile(typeof(Properties)), _verifySettings); 100 | } 101 | 102 | [TestMethod] 103 | public Task Record() 104 | { 105 | return Verify(Decompile(typeof(Record)), _verifySettings); 106 | } 107 | 108 | [TestMethod] 109 | public Task Returns() 110 | { 111 | return Verify(Decompile(typeof(Returns)), _verifySettings); 112 | } 113 | 114 | [TestMethod] 115 | public Task TaskResults() 116 | { 117 | return Verify(Decompile(typeof(TaskResults)), _verifySettings); 118 | } 119 | 120 | // TestAssemblyNonPublic: 121 | 122 | [TestMethod] 123 | public Task InternalSubClass() 124 | { 125 | return Verify(Decompile(typeof(InternalSubClass)), _verifySettings); 126 | } 127 | 128 | [TestMethod] 129 | public Task PublicBaseClass() 130 | { 131 | return Verify(Decompile(typeof(PublicBaseClass)), _verifySettings); 132 | } 133 | 134 | [TestMethod] 135 | public Task PublicContainingNestedNonPublic() 136 | { 137 | return Verify(Decompile(typeof(PublicContainingNestedNonPublic)), _verifySettings); 138 | } 139 | 140 | // TestPointers: 141 | 142 | [TestMethod] 143 | public Task Pointers() 144 | { 145 | return Verify(Decompile(typeof(Pointers)), _verifySettings); 146 | } 147 | 148 | // TestAssemblyThrowHelpers: 149 | 150 | [TestMethod] 151 | public Task UsesThrowHelpers() 152 | { 153 | return Verify(Decompile(typeof(UsesThrowHelpers)), _verifySettings); 154 | } 155 | 156 | #pragma warning disable CS0618 // Type or member is obsolete 157 | private static string Decompile(Type type) => Ildasm.Decompile(type.Assembly.Location, type.FullName); 158 | #pragma warning restore CS0618 // Type or member is obsolete 159 | } -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.Returns.Debug.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.Returns 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig static string 7 | GoodReference() cil managed 8 | { 9 | .maxstack 8 10 | IL_0000: ldsfld string [System.Runtime]System.String::Empty 11 | IL_0005: dup 12 | IL_0006: brtrue.s IL_0012 13 | IL_0008: ldstr "Return value nullability contract was broken." 14 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 15 | IL_0012: ret 16 | } 17 | .method public hidebysig static string 18 | BadReference() cil managed 19 | { 20 | .maxstack 8 21 | IL_0000: ldnull 22 | IL_0001: dup 23 | IL_0002: brtrue.s IL_000e 24 | IL_0004: ldstr "Return value nullability contract was broken." 25 | IL_0009: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 26 | IL_000e: ret 27 | } 28 | .method public hidebysig static string 29 | NullableReference() cil managed 30 | { 31 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 32 | .maxstack 8 33 | IL_0000: ldnull 34 | IL_0001: ret 35 | } 36 | .method public hidebysig static string 37 | NoNullChecksBadReference() cil managed 38 | { 39 | .maxstack 8 40 | IL_0000: ldnull 41 | IL_0001: ret 42 | } 43 | .method public hidebysig static string 44 | NullChecksOnNoNullChecksMethodBadReference() cil managed 45 | { 46 | .maxstack 8 47 | IL_0000: ldnull 48 | IL_0001: dup 49 | IL_0002: brtrue.s IL_000e 50 | IL_0004: ldstr "Return value nullability contract was broken." 51 | IL_0009: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 52 | IL_000e: ret 53 | } 54 | .method public hidebysig static !!T GoodGenericNotNullConstraint<.ctor T>() cil managed 55 | { 56 | .maxstack 8 57 | IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance() 58 | IL_0005: dup 59 | IL_0006: box !!T 60 | IL_000b: brtrue.s IL_0017 61 | IL_000d: ldstr "Return value nullability contract was broken." 62 | IL_0012: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 63 | IL_0017: ret 64 | } 65 | .method public hidebysig static !!T BadGenericNotNullConstraint<.ctor T>() cil managed 66 | { 67 | .maxstack 2 68 | .locals init (!!T V_0) 69 | IL_0000: ldloca.s V_0 70 | IL_0002: initobj !!T 71 | IL_0008: ldloc.0 72 | IL_0009: dup 73 | IL_000a: box !!T 74 | IL_000f: brtrue.s IL_001b 75 | IL_0011: ldstr "Return value nullability contract was broken." 76 | IL_0016: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 77 | IL_001b: ret 78 | } 79 | .method public hidebysig static !!T GoodGenericNotNullAttribute<.ctor T>() cil managed 80 | { 81 | .param type T 82 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 83 | .param [0] 84 | .maxstack 8 85 | IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance() 86 | IL_0005: dup 87 | IL_0006: box !!T 88 | IL_000b: brtrue.s IL_0017 89 | IL_000d: ldstr "Return value nullability contract was broken." 90 | IL_0012: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 91 | IL_0017: ret 92 | } 93 | .method public hidebysig static !!T BadGenericNotNullAttribute() cil managed 94 | { 95 | .param type T 96 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 97 | .param [0] 98 | .maxstack 2 99 | .locals init (!!T V_0) 100 | IL_0000: ldloca.s V_0 101 | IL_0002: initobj !!T 102 | IL_0008: ldloc.0 103 | IL_0009: dup 104 | IL_000a: box !!T 105 | IL_000f: brtrue.s IL_001b 106 | IL_0011: ldstr "Return value nullability contract was broken." 107 | IL_0016: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 108 | IL_001b: ret 109 | } 110 | .method public hidebysig static !!T GenericMaybeNullAttribute() cil managed 111 | { 112 | .param [0] 113 | .maxstack 1 114 | .locals init (!!T V_0) 115 | IL_0000: ldloca.s V_0 116 | IL_0002: initobj !!T 117 | IL_0008: ldloc.0 118 | IL_0009: ret 119 | } 120 | .method public hidebysig static !!T GenericUnconstrained() cil managed 121 | { 122 | .param type T 123 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 124 | .maxstack 1 125 | .locals init (!!T V_0) 126 | IL_0000: ldloca.s V_0 127 | IL_0002: initobj !!T 128 | IL_0008: ldloc.0 129 | IL_0009: ret 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.Returns.Debug.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.Returns 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig static string 7 | GoodReference() cil managed 8 | { 9 | .maxstack 8 10 | IL_0000: ldsfld string [System.Runtime]System.String::Empty 11 | IL_0005: dup 12 | IL_0006: brtrue.s IL_0012 13 | IL_0008: ldstr "Return value nullability contract was broken." 14 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 15 | IL_0012: ret 16 | } 17 | .method public hidebysig static string 18 | BadReference() cil managed 19 | { 20 | .maxstack 8 21 | IL_0000: ldnull 22 | IL_0001: dup 23 | IL_0002: brtrue.s IL_000e 24 | IL_0004: ldstr "Return value nullability contract was broken." 25 | IL_0009: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 26 | IL_000e: ret 27 | } 28 | .method public hidebysig static string 29 | NullableReference() cil managed 30 | { 31 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 32 | .maxstack 8 33 | IL_0000: ldnull 34 | IL_0001: ret 35 | } 36 | .method public hidebysig static string 37 | NoNullChecksBadReference() cil managed 38 | { 39 | .maxstack 8 40 | IL_0000: ldnull 41 | IL_0001: ret 42 | } 43 | .method public hidebysig static string 44 | NullChecksOnNoNullChecksMethodBadReference() cil managed 45 | { 46 | .maxstack 8 47 | IL_0000: ldnull 48 | IL_0001: dup 49 | IL_0002: brtrue.s IL_000e 50 | IL_0004: ldstr "Return value nullability contract was broken." 51 | IL_0009: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 52 | IL_000e: ret 53 | } 54 | .method public hidebysig static !!T GoodGenericNotNullConstraint<.ctor T>() cil managed 55 | { 56 | .maxstack 8 57 | IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance() 58 | IL_0005: dup 59 | IL_0006: box !!T 60 | IL_000b: brtrue.s IL_0017 61 | IL_000d: ldstr "Return value nullability contract was broken." 62 | IL_0012: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 63 | IL_0017: ret 64 | } 65 | .method public hidebysig static !!T BadGenericNotNullConstraint<.ctor T>() cil managed 66 | { 67 | .maxstack 2 68 | .locals init (!!T V_0) 69 | IL_0000: ldloca.s V_0 70 | IL_0002: initobj !!T 71 | IL_0008: ldloc.0 72 | IL_0009: dup 73 | IL_000a: box !!T 74 | IL_000f: brtrue.s IL_001b 75 | IL_0011: ldstr "Return value nullability contract was broken." 76 | IL_0016: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 77 | IL_001b: ret 78 | } 79 | .method public hidebysig static !!T GoodGenericNotNullAttribute<.ctor T>() cil managed 80 | { 81 | .param type T 82 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 83 | .param [0] 84 | .maxstack 8 85 | IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance() 86 | IL_0005: dup 87 | IL_0006: box !!T 88 | IL_000b: brtrue.s IL_0017 89 | IL_000d: ldstr "Return value nullability contract was broken." 90 | IL_0012: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 91 | IL_0017: ret 92 | } 93 | .method public hidebysig static !!T BadGenericNotNullAttribute() cil managed 94 | { 95 | .param type T 96 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 97 | .param [0] 98 | .maxstack 2 99 | .locals init (!!T V_0) 100 | IL_0000: ldloca.s V_0 101 | IL_0002: initobj !!T 102 | IL_0008: ldloc.0 103 | IL_0009: dup 104 | IL_000a: box !!T 105 | IL_000f: brtrue.s IL_001b 106 | IL_0011: ldstr "Return value nullability contract was broken." 107 | IL_0016: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 108 | IL_001b: ret 109 | } 110 | .method public hidebysig static !!T GenericMaybeNullAttribute() cil managed 111 | { 112 | .param [0] 113 | .maxstack 1 114 | .locals init (!!T V_0) 115 | IL_0000: ldloca.s V_0 116 | IL_0002: initobj !!T 117 | IL_0008: ldloc.0 118 | IL_0009: ret 119 | } 120 | .method public hidebysig static !!T GenericUnconstrained() cil managed 121 | { 122 | .param type T 123 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 124 | .maxstack 1 125 | .locals init (!!T V_0) 126 | IL_0000: ldloca.s V_0 127 | IL_0002: initobj !!T 128 | IL_0008: ldloc.0 129 | IL_0009: ret 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.Returns.Release.DotNet6_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.Returns 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig static string 7 | GoodReference() cil managed 8 | { 9 | .maxstack 8 10 | IL_0000: ldsfld string [System.Runtime]System.String::Empty 11 | IL_0005: dup 12 | IL_0006: brtrue.s IL_0012 13 | IL_0008: ldstr "Return value nullability contract was broken." 14 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 15 | IL_0012: ret 16 | } 17 | .method public hidebysig static string 18 | BadReference() cil managed 19 | { 20 | .maxstack 8 21 | IL_0000: ldnull 22 | IL_0001: dup 23 | IL_0002: brtrue.s IL_000e 24 | IL_0004: ldstr "Return value nullability contract was broken." 25 | IL_0009: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 26 | IL_000e: ret 27 | } 28 | .method public hidebysig static string 29 | NullableReference() cil managed 30 | { 31 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 32 | .maxstack 8 33 | IL_0000: ldnull 34 | IL_0001: ret 35 | } 36 | .method public hidebysig static string 37 | NoNullChecksBadReference() cil managed 38 | { 39 | .maxstack 8 40 | IL_0000: ldnull 41 | IL_0001: ret 42 | } 43 | .method public hidebysig static string 44 | NullChecksOnNoNullChecksMethodBadReference() cil managed 45 | { 46 | .maxstack 8 47 | IL_0000: ldnull 48 | IL_0001: dup 49 | IL_0002: brtrue.s IL_000e 50 | IL_0004: ldstr "Return value nullability contract was broken." 51 | IL_0009: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 52 | IL_000e: ret 53 | } 54 | .method public hidebysig static !!T GoodGenericNotNullConstraint<.ctor T>() cil managed 55 | { 56 | .maxstack 8 57 | IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance() 58 | IL_0005: dup 59 | IL_0006: box !!T 60 | IL_000b: brtrue.s IL_0017 61 | IL_000d: ldstr "Return value nullability contract was broken." 62 | IL_0012: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 63 | IL_0017: ret 64 | } 65 | .method public hidebysig static !!T BadGenericNotNullConstraint<.ctor T>() cil managed 66 | { 67 | .maxstack 2 68 | .locals init (!!T V_0) 69 | IL_0000: ldloca.s V_0 70 | IL_0002: initobj !!T 71 | IL_0008: ldloc.0 72 | IL_0009: dup 73 | IL_000a: box !!T 74 | IL_000f: brtrue.s IL_001b 75 | IL_0011: ldstr "Return value nullability contract was broken." 76 | IL_0016: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 77 | IL_001b: ret 78 | } 79 | .method public hidebysig static !!T GoodGenericNotNullAttribute<.ctor T>() cil managed 80 | { 81 | .param type T 82 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 83 | .param [0] 84 | .maxstack 8 85 | IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance() 86 | IL_0005: dup 87 | IL_0006: box !!T 88 | IL_000b: brtrue.s IL_0017 89 | IL_000d: ldstr "Return value nullability contract was broken." 90 | IL_0012: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 91 | IL_0017: ret 92 | } 93 | .method public hidebysig static !!T BadGenericNotNullAttribute() cil managed 94 | { 95 | .param type T 96 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 97 | .param [0] 98 | .maxstack 2 99 | .locals init (!!T V_0) 100 | IL_0000: ldloca.s V_0 101 | IL_0002: initobj !!T 102 | IL_0008: ldloc.0 103 | IL_0009: dup 104 | IL_000a: box !!T 105 | IL_000f: brtrue.s IL_001b 106 | IL_0011: ldstr "Return value nullability contract was broken." 107 | IL_0016: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 108 | IL_001b: ret 109 | } 110 | .method public hidebysig static !!T GenericMaybeNullAttribute() cil managed 111 | { 112 | .param [0] 113 | .maxstack 1 114 | .locals init (!!T V_0) 115 | IL_0000: ldloca.s V_0 116 | IL_0002: initobj !!T 117 | IL_0008: ldloc.0 118 | IL_0009: ret 119 | } 120 | .method public hidebysig static !!T GenericUnconstrained() cil managed 121 | { 122 | .param type T 123 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 124 | .maxstack 1 125 | .locals init (!!T V_0) 126 | IL_0000: ldloca.s V_0 127 | IL_0002: initobj !!T 128 | IL_0008: ldloc.0 129 | IL_0009: ret 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Source/RuntimeNullables.Fody.Tests/VerifyILTests.Returns.Release.DotNet7_0.verified.txt: -------------------------------------------------------------------------------- 1 | .class public abstract auto ansi sealed beforefieldinit TestAssembly.Returns 2 | extends [System.Runtime]System.Object 3 | { 4 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) 5 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 00 00 00 ) 6 | .method public hidebysig static string 7 | GoodReference() cil managed 8 | { 9 | .maxstack 8 10 | IL_0000: ldsfld string [System.Runtime]System.String::Empty 11 | IL_0005: dup 12 | IL_0006: brtrue.s IL_0012 13 | IL_0008: ldstr "Return value nullability contract was broken." 14 | IL_000d: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 15 | IL_0012: ret 16 | } 17 | .method public hidebysig static string 18 | BadReference() cil managed 19 | { 20 | .maxstack 8 21 | IL_0000: ldnull 22 | IL_0001: dup 23 | IL_0002: brtrue.s IL_000e 24 | IL_0004: ldstr "Return value nullability contract was broken." 25 | IL_0009: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 26 | IL_000e: ret 27 | } 28 | .method public hidebysig static string 29 | NullableReference() cil managed 30 | { 31 | .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 32 | .maxstack 8 33 | IL_0000: ldnull 34 | IL_0001: ret 35 | } 36 | .method public hidebysig static string 37 | NoNullChecksBadReference() cil managed 38 | { 39 | .maxstack 8 40 | IL_0000: ldnull 41 | IL_0001: ret 42 | } 43 | .method public hidebysig static string 44 | NullChecksOnNoNullChecksMethodBadReference() cil managed 45 | { 46 | .maxstack 8 47 | IL_0000: ldnull 48 | IL_0001: dup 49 | IL_0002: brtrue.s IL_000e 50 | IL_0004: ldstr "Return value nullability contract was broken." 51 | IL_0009: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 52 | IL_000e: ret 53 | } 54 | .method public hidebysig static !!T GoodGenericNotNullConstraint<.ctor T>() cil managed 55 | { 56 | .maxstack 8 57 | IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance() 58 | IL_0005: dup 59 | IL_0006: box !!T 60 | IL_000b: brtrue.s IL_0017 61 | IL_000d: ldstr "Return value nullability contract was broken." 62 | IL_0012: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 63 | IL_0017: ret 64 | } 65 | .method public hidebysig static !!T BadGenericNotNullConstraint<.ctor T>() cil managed 66 | { 67 | .maxstack 2 68 | .locals init (!!T V_0) 69 | IL_0000: ldloca.s V_0 70 | IL_0002: initobj !!T 71 | IL_0008: ldloc.0 72 | IL_0009: dup 73 | IL_000a: box !!T 74 | IL_000f: brtrue.s IL_001b 75 | IL_0011: ldstr "Return value nullability contract was broken." 76 | IL_0016: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 77 | IL_001b: ret 78 | } 79 | .method public hidebysig static !!T GoodGenericNotNullAttribute<.ctor T>() cil managed 80 | { 81 | .param type T 82 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 83 | .param [0] 84 | .maxstack 8 85 | IL_0000: call !!0 [System.Runtime]System.Activator::CreateInstance() 86 | IL_0005: dup 87 | IL_0006: box !!T 88 | IL_000b: brtrue.s IL_0017 89 | IL_000d: ldstr "Return value nullability contract was broken." 90 | IL_0012: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 91 | IL_0017: ret 92 | } 93 | .method public hidebysig static !!T BadGenericNotNullAttribute() cil managed 94 | { 95 | .param type T 96 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 97 | .param [0] 98 | .maxstack 2 99 | .locals init (!!T V_0) 100 | IL_0000: ldloca.s V_0 101 | IL_0002: initobj !!T 102 | IL_0008: ldloc.0 103 | IL_0009: dup 104 | IL_000a: box !!T 105 | IL_000f: brtrue.s IL_001b 106 | IL_0011: ldstr "Return value nullability contract was broken." 107 | IL_0016: call void RuntimeNullables.ThrowHelpers::ThrowOutputNull(string) 108 | IL_001b: ret 109 | } 110 | .method public hidebysig static !!T GenericMaybeNullAttribute() cil managed 111 | { 112 | .param [0] 113 | .maxstack 1 114 | .locals init (!!T V_0) 115 | IL_0000: ldloca.s V_0 116 | IL_0002: initobj !!T 117 | IL_0008: ldloc.0 118 | IL_0009: ret 119 | } 120 | .method public hidebysig static !!T GenericUnconstrained() cil managed 121 | { 122 | .param type T 123 | .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) 124 | .maxstack 1 125 | .locals init (!!T V_0) 126 | IL_0000: ldloca.s V_0 127 | IL_0002: initobj !!T 128 | IL_0008: ldloc.0 129 | IL_0009: ret 130 | } 131 | } 132 | --------------------------------------------------------------------------------