WoofWare.FSharpAnalyzers 0.2.3

There is a newer version of this package available.
See the version list below for details.
dotnet add package WoofWare.FSharpAnalyzers --version 0.2.3
                    
NuGet\Install-Package WoofWare.FSharpAnalyzers -Version 0.2.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="WoofWare.FSharpAnalyzers" Version="0.2.3">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="WoofWare.FSharpAnalyzers" Version="0.2.3" />
                    
Directory.Packages.props
<PackageReference Include="WoofWare.FSharpAnalyzers">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add WoofWare.FSharpAnalyzers --version 0.2.3
                    
#r "nuget: WoofWare.FSharpAnalyzers, 0.2.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package WoofWare.FSharpAnalyzers@0.2.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=WoofWare.FSharpAnalyzers&version=0.2.3
                    
Install as a Cake Addin
#tool nuget:?package=WoofWare.FSharpAnalyzers&version=0.2.3
                    
Install as a Cake Tool

WoofWare.FSharpAnalyzers

A set of F# source analyzers, using the Ionide analyzer SDK.

They are modelled on the G-Research analyzers, but are much more opinionated. They're intended for my personal use.

Analyzers

BlockingCallsAnalyzer

Bans the use of blocking calls like Async.RunSynchronously.

You will have to have a blocking call in your main method; use the suppression comment "fsharpanalyzer: ignore-line WOOF-BLOCKING" to guard that line.

Rationale

Prevent sync-over-async.

ReferenceEqualsAnalyzer

Bans the use of Object.ReferenceEquals.

Use the suppression comment "fsharpanalyzer: ignore-line WOOF-REFEQUALS" to suppress the analyzer. (If you define a type-safe version of ReferenceEquals - see the next section - then you will have to specify the suppression inside that function.)

Rationale

Object.ReferenceEquals has two significant problems:

  1. It silently does the wrong thing on value types. When you pass value types to Object.ReferenceEquals, they get boxed, and the function compares the boxed instances rather than the original values. This means Object.ReferenceEquals(42, 42) will always return false, which is rarely what you want.

  2. It lacks type safety. The function accepts any two objects, making it too easy to accidentally compare objects of completely different types (e.g., Object.ReferenceEquals("hello", 42)), which will always return false but compiles without warning.

Instead, use a type-safe wrapper that enforces reference type constraints and type consistency:

let referenceEquals<'a when 'a : not struct> (x : 'a) (y : 'a) : bool =
    obj.ReferenceEquals(x, y)

This prevents both issues: the not struct constraint prevents value types from being passed, and the type parameter 'a ensures both arguments are the same type.

TaskCompletionSourceAnalyzer

Requires TaskCompletionSource<T> to be created with TaskCreationOptions.RunContinuationsAsynchronously.

Use the suppression comment fsharpanalyzer: ignore-line WOOF-TCS-ASYNC to suppress the analyzer.

Rationale

By default, when you call SetResult, SetException, or SetCanceled on a TaskCompletionSource<T>, any continuations attached to the resulting task will run synchronously on the thread that completes the task. This can lead to serious problems:

  1. Deadlocks: If the continuation tries to acquire a lock or synchronization context that the calling thread holds, you get a deadlock.

  2. Thread-pool starvation: Continuations may perform long-running work, blocking the thread that called SetResult and preventing it from doing other work.

  3. State corruption: The continuation runs with the calling thread's execution context, which may have unexpected side effects like running on a UI thread or within a specific synchronization context.

Always create TaskCompletionSource<T> with TaskCreationOptions.RunContinuationsAsynchronously to ensure continuations are scheduled asynchronously on the thread pool:

let tcs = TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously)

ThrowingInDisposeAnalyzer

Bans throwing in a Dispose implementation.

Use the suppression comment "fsharpanalyzer: ignore-line WOOF-THROWING-DISPOSE" to suppress the analyzer.

Rationale

See the C# analyzer. Basically, users find it very confusing when a finally clause still needs exception handling inside it.

Any Dispose (isDisposing = false) code path (conventionally called by the finaliser thread) is especially bad to throw in, because such errors are completely recoverable.

Licence

WoofWare.FSharpAnalyzers is licensed to you under the MIT licence, a copy of which can be found at LICENSE.md.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.9 212 11/25/2025
0.2.8 186 11/25/2025
0.2.7 179 11/24/2025
0.2.6 183 11/23/2025
0.2.5 214 11/23/2025
0.2.4 148 11/23/2025
0.2.3 397 11/20/2025
0.2.2 354 11/12/2025
0.2.1 215 10/20/2025
0.1.5 176 10/19/2025
0.1.4 175 10/19/2025
0.1.3 621 10/4/2025
0.1.2 128 10/3/2025
0.1.1 205 10/3/2025