Skip to content

Latest commit

History

1,115 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

NullGuard.Fody

Chat on GitterNuGet Status

See Milestones for release notes.

This is an add-in for Fody

It is expected that all developers using Fody become a Patron on OpenCollective. See Licensing/Patron FAQ for more information.

Usage

See also Fody usage.

NuGet installation

Install the NullGuard.Fody NuGet package and update the Fody NuGet package:

PM>Install-Package Fody
PM>Install-Package NullGuard.Fody

The Install-Package Fody is required since NuGet always defaults to the oldest, and most buggy, version of any dependency.

Modes

NullGuard supports three modes of operations, implicit, explicit and nullable reference types.

  • In implicit mode everything is assumed to be not-null, unless attributed with [AllowNull]. This is how NullGuard has been working always.
  • In explicit mode everything is assumed to be nullable, unless attributed with [NotNull]. This mode is designed to support the R# nullability analysis, using pessimistic mode.
  • In nullable reference types mode the C# 8 nullable reference type (NRT) annotations are used to determine if a type may be null.

If not configured explicitly, NullGuard will auto-detect the mode as follows:

  • If C# 8 nullable attributes are detected then nullable reference types mode is used.
  • Referencing JetBrains.Annotations and using [NotNull] anywhere will switch to explicit mode.
  • Default to implicit mode if the above criteria is not met.

Implicit Mode

Your Code
publicclassSample{publicvoidSomeMethod(stringarg){// throws ArgumentNullException if arg is null.}publicvoidAnotherMethod([AllowNull]stringarg){// arg may be null here}publicvoidAndAnotherMethod(string?arg){// arg may be null here}publicstringMethodWithReturn(){returnSomeOtherClass.SomeMethod();}[return:AllowNull]publicstringMethodAllowsNullReturnValue(){returnnull;}publicstring?MethodAlsoAllowsNullReturnValue(){returnnull;}// Null checking works for automatic properties too.publicstringSomeProperty{get;set;}// can be applied to a whole property[AllowNull]publicstringNullProperty{get;set;}// Or just the setter.publicstringNullPropertyOnSet{get;[param:AllowNull]set;}}
What gets compiled
publicclassSampleOutput{publicstringNullProperty{get;set}stringnullPropertyOnSet;publicstringNullPropertyOnSet{get{varreturnValue=nullPropertyOnSet;if(returnValue==null){thrownewInvalidOperationException("Return value of property 'NullPropertyOnSet' is null.");}returnreturnValue;}set{nullPropertyOnSet=value;}}publicstringMethodAllowsNullReturnValue(){returnnull;}publicstringMethodAlsoAllowsNullReturnValue(){returnnull;}stringsomeProperty;publicstringSomeProperty{get{if(someProperty==null){thrownewInvalidOperationException("Return value of property 'SomeProperty' is null.");}returnsomeProperty;}set{if(value==null){thrownewArgumentNullException("value","Cannot set the value of property 'SomeProperty' to null.");}someProperty=value;}}publicvoidAnotherMethod(stringarg){}publicvoidAndAnotherMethod(stringarg){}publicstringMethodWithReturn(){varreturnValue=SomeOtherClass.SomeMethod();if(returnValue==null){thrownewInvalidOperationException("Return value of method 'MethodWithReturn' is null.");}returnreturnValue;}publicvoidSomeMethod(stringarg){if(arg==null){thrownewArgumentNullException("arg");}}}

Explicit Mode

If you are (already) using R#'s [NotNull] attribute in your code to explicitly annotate not null items, null guards will be added only for items that have an explicit [NotNull] annotation.

publicclassSample{publicvoidSomeMethod([NotNull]stringarg){// throws ArgumentNullException if arg is null.}publicvoidAnotherMethod(stringarg){// arg may be null here}[NotNull]publicstringMethodWithReturn(){returnSomeOtherClass.SomeMethod();}publicstringMethodAllowsNullReturnValue(){returnnull;}// Null checking works for automatic properties too.// Default in explicit mode is nullablepublicstringNullProperty{get;set;}// NotNull can be applied to a whole property[NotNull]publicstringSomeProperty{get;set;}// or just the getter by overwriting the set method,[NotNull]publicstringNullPropertyOnSet{get;[param:AllowNull]set;}// or just the setter by overwriting the get method.[NotNull]publicstringNullPropertyOnGet{[return:AllowNull]get;set;}}

Inheritance of nullability is supported in explicit mode, i.e. if you implement an interface or derive from a base method with [NotNull] annotations, null guards will be added to your implementation.

You may use the [NotNull] attribute defined in JetBrains.Anntotations, or simply define your own. However not referencing JetBrains.Anntotations will not auto-detect explicit mode, so you have to set this in the configuration.

Also note that using JetBrains.Anntotations will require to define JETBRAINS_ANNOTATIONS to include the attributes in the assembly, so NullGuard can find them. NullGuard will neither remove those attributes nor the reference to JetBrains.Anntotations. To get rid of the attributes and the reference, you can use JetBrainsAnnotations.Fody. Just make sure NullGuard will run prior to JetBrainsAnnotations.Fody.

Nullable Reference Types Mode

Standard NRT annotations and attributes are used to determine the nullability of a type. Conditional postcondition attributes (ie. [MaybeNullWhenAttribute]) that indicate the value may sometimes be null causes the postcondition null check to be omitted.

publicclassSample{// Allows null return valuespublicstring?MaybeGetValue(){returnnull;}// Throws InvalidOperationException since return value is not nullablepublicstringMustReturnValue(){returnnull;}// Throws InvalidOperationException for task results that violate nullability as wellpublicasyncTask<string>GetValueAsync(){returnnull;}// Allows null task resultpublicasyncTask<string?>GetValueAsync(){returnnull;}publicvoidWriteValue(stringarg){// throws ArgumentNullException if arg is null.}publicvoidWriteValue(string?arg){// arg may be null here}publicvoidGenericMethod<T>(Targ)whereT:notnull{// throws ArgumentNullException if arg is null.}publicboolTryGetValue<T>(stringkey,[MaybeNullWhen(false)]outTvalue){// throws ArgumentNullException if key is null.// out value is not checked.}}

See the documentation for more information on the available nullable reference type attributes.

NullGuard adds a special annotation [MaybeNullTaskResultAttribute] for this mode that can be used to control whether a Task result value might be null in situations where this currently isn't possible with NRTs:

// Throws InvalidOperationException for reference typed T unless the return value // is marked with [MaybeNullTaskResult].[return:MaybeNullTaskResult]publicasyncTask<T>TryGetValueAsync<T>()whereT:notnull{returndefault(T);}

Attributes

Where and how injection occurs can be controlled via attributes. The NullGuard.Fody nuget ships with an assembly containing these attributes.

/// <summary>/// Prevents the injection of null checking (implicit mode only)./// </summary>[AttributeUsage(AttributeTargets.Parameter|AttributeTargets.ReturnValue|AttributeTargets.Property)]publicclassAllowNullAttribute:Attribute{}/// <summary>/// Prevents injection of null checking on task result values when return value checks are enabled (NRT mode only)./// </summary>[AttributeUsage(AttributeTargets.ReturnValue)]publicclassMaybeNullTaskResultAttribute:Attribute{}/// <summary>/// Allow specific categories of members to be targeted for injection. <seealso cref="ValidationFlags"/>/// </summary>[AttributeUsage(AttributeTargets.Assembly|AttributeTargets.Class)]publicclassNullGuardAttribute:Attribute{/// <summary>/// Initializes a new instance of the <see cref="NullGuardAttribute"/> with a <see cref="ValidationFlags"/>./// </summary>/// <param name="flags">The <see cref="ValidationFlags"/> to use for the target this attribute is being applied to.</param>publicNullGuardAttribute(ValidationFlagsflags){}}/// <summary>/// Used by <see cref="NullGuardAttribute"/> to target specific categories of members./// </summary>[Flags]publicenumValidationFlags{None=0,Properties=1,Arguments=2,OutValues=4,ReturnValues=8,NonPublic=16,Methods=Arguments|OutValues|ReturnValues,AllPublicArguments=Properties|Arguments,AllPublic=Properties|Methods,All=AllPublic|NonPublic}

All NullGuard attributes are removed from the assembly as part of the build.

Attributes are checked locally at the member, and if there are no attributes then the class is checked. If the class has no attributes then the assembly is checked. Finally if there are no attributes at the assembly level then the default value is used.

NullGuardAttribute

NullGuardAttribute can be used at the class or assembly level. It takes a ValidationFlags parameter.

[assembly:NullGuard(ValidationFlags.None)]// Sets no guards at the assembly level[NullGuard(ValidationFlags.AllPublicArguments)]// Sets the default guard for class FoopublicclassFoo{ ...}

ValidationFlags

The ValidationFlags determine how much checking NullGuard adds to your assembly.

  • None Does nothing.
  • Properties Adds null guard checks to properties getter (cannot return null) and setter (cannot be set to null).
  • Arguments Method arguments are checked to make sure they are not null. This only applies to normal arguments, and the incoming value of a ref argument.
  • OutValues Out and ref arguments of a method are checked for null just before the method returns.
  • ReturnValues Checks the return value of a method for null.
  • NonPublic Applies the other flags to all non-public members as well.
  • Methods Processes all arguments (normal, out and ref) and return values of methods.
  • AllPublicArguments Processes all methods (arguments and return values) and properties.
  • AllPublic Checks everything (properties, all method args and return values).

AllowNullAttribute and CanBeNullAttribute

These attributes allow you to specify which arguments, return values and properties can be set to null. AllowNullAttribute comes from the referenced project NullGuard adds. CanBeNullAttribute can come from anywhere, but is commonly used by Resharper.

[AllowNull]publicstringNullProperty{get;set;}publicvoidSomeMethod(stringnonNullArg,[AllowNull]stringnullArg){ ...}[return:AllowNull]publicstringMethodAllowsNullReturnValue(){ ...}publicstringPropertyAllowsNullGetButDoesNotAllowNullSet{[return:AllowNull]get;set;}publicstringPropertyAllowsNullSetButDoesNotAllowNullGet{get;[param:AllowNull] set;}

Configuration

For Release builds NullGuard will weave code that throws ArgumentNullException. For Debug builds NullGuard weaves Debug.Assert. If you want ArgumentNullException to be thrown for Debug builds then update FodyWeavers.xml to include:

<NullGuardIncludeDebugAssert="false" />

A complete example of FodyWeavers.xml looks like this:

<Weavers>
<NullGuardIncludeDebugAssert="false" />
</Weavers>

You can also use RegEx to specify the name of a class to exclude from NullGuard.

<NullGuardExcludeRegex="^ClassToExclude$" />

You can force the operation mode by setting it to Explicit, Implicit or NullableReferenceTypes, if the default AutoDetect does not detect the usage correctly.

<NullGuardMode="Explicit" />

Icon

Icon courtesy of The Noun Project

About

Adds null argument checks to an assembly

Resources

Security policy

Stars

680 stars

Watchers

10 watching

Forks

Sponsor this project

Used by

Contributors

Languages