FunctionalDdd.Analyzers 3.0.0-alpha.60

This is a prerelease version of FunctionalDdd.Analyzers.
dotnet add package FunctionalDdd.Analyzers --version 3.0.0-alpha.60
                    
NuGet\Install-Package FunctionalDdd.Analyzers -Version 3.0.0-alpha.60
                    
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="FunctionalDdd.Analyzers" Version="3.0.0-alpha.60" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FunctionalDdd.Analyzers" Version="3.0.0-alpha.60" />
                    
Directory.Packages.props
<PackageReference Include="FunctionalDdd.Analyzers" />
                    
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 FunctionalDdd.Analyzers --version 3.0.0-alpha.60
                    
#r "nuget: FunctionalDdd.Analyzers, 3.0.0-alpha.60"
                    
#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 FunctionalDdd.Analyzers@3.0.0-alpha.60
                    
#: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=FunctionalDdd.Analyzers&version=3.0.0-alpha.60&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=FunctionalDdd.Analyzers&version=3.0.0-alpha.60&prerelease
                    
Install as a Cake Tool

FunctionalDdd.Analyzers

Roslyn analyzers that enforce proper usage of Result and Maybe patterns at compile time.

Overview

This package provides compile-time analysis to help you write safer, more maintainable code with FunctionalDdd's Railway Oriented Programming patterns.

Installation

dotnet add package FunctionalDdd.Analyzers

The analyzers are automatically integrated into your build process and will provide warnings/errors in Visual Studio, VS Code, and during dotnet build.

What It Analyzes

Result Pattern Enforcement

The analyzers help prevent common mistakes when working with Result<T>:

  • Unsafe value access (FDDD003, FDDD004) - Ensures you check IsSuccess/IsFailure before accessing Value/Error
  • Unhandled results (FDDD001) - Warns when Result types are returned but not handled
  • Double wrapping (FDDD008) - Detects Result<Result<T>> patterns (usually indicates Map should be Bind)
  • Wrong method usage (FDDD002) - Suggests using Bind instead of Map when lambda returns Result
  • Async misuse (FDDD009) - Prevents blocking on Task<Result<T>> with .Result or .Wait()
  • Async lambda misuse (FDDD014) - Detects async lambda used with sync method (Map instead of MapAsync)
  • TryCreate().Value anti-pattern (FDDD007) - Suggests using .Create() for clearer errors
  • Manual result combination (FDDD012) - Suggests using Result.Combine() for multiple validations
  • Ternary operator patterns (FDDD013) - Suggests using GetValueOrDefault() or Match()
  • Throwing in Result chains (FDDD015) - Detects throw statements that defeat ROP semantics
  • Null comparisons (FDDD017) - Detects comparing Result/Maybe to null (they're structs)
  • Unsafe LINQ access (FDDD018) - Detects .Value in LINQ without filtering by IsSuccess/HasValue

Error Handling Best Practices

  • Specific error types (FDDD010) - Encourages using Error.Validation(), Error.NotFound() etc. instead of base Error class
  • Error discrimination (FDDD005) - Suggests using MatchError for type-safe error handling
  • Empty error messages (FDDD016) - Detects empty or missing error messages

Maybe Pattern Enforcement

Similar protections for Maybe<T>:

  • Unsafe access (FDDD006) - Ensures proper checking before accessing Maybe<T>.Value
  • Double wrapping (FDDD011) - Detects Maybe<Maybe<T>> patterns

Example Diagnostics

FDDD003: Unsafe Result.Value Access

Problem: Accessing .Value without checking success state can throw exceptions.

// ❌ Warning: Accessing Value without checking IsSuccess
var user = GetUser(id);
Console.WriteLine(user.Value.Name);  // FDDD003: May throw InvalidOperationException

// ✅ Option 1: Check before access
var user = GetUser(id);
if (user.IsSuccess)
    Console.WriteLine(user.Value.Name);

// ✅ Option 2: Use TryGetValue pattern
if (GetUser(id).TryGetValue(out var user))
    Console.WriteLine(user.Name);

// ✅ Option 3: Use Match (recommended)
GetUser(id).Match(
    onSuccess: u => Console.WriteLine(u.Name),
    onFailure: err => Console.WriteLine($"Error: {err.Detail}")
);

FDDD002: Use Bind Instead of Map

Problem: Using Map when the lambda returns a Result creates double-wrapped Result<Result<T>>.

// ❌ Creates Result<Result<Order>> 
var result = userId.Map(id => GetOrder(id));  // FDDD002

// ✅ Use Bind for flattening
var result = userId.Bind(id => GetOrder(id));  // Result<Order>

FDDD007: TryCreate().Value Anti-Pattern

Problem: TryCreate().Value provides poor error messages when validation fails.

// ❌ Unclear error message on failure
var email = EmailAddress.TryCreate(input).Value;  // FDDD007

// ✅ Use Create() for expected-valid values (better error message)
var email = EmailAddress.Create(input);

// ✅ Or handle the Result properly
var emailResult = EmailAddress.TryCreate(input);
if (emailResult.IsFailure)
    return BadRequest(emailResult.Error);
var email = emailResult.Value;

FDDD008: Double-Wrapped Result

Problem: Result<Result<T>> is almost always unintended.

// ❌ Creates Result<Result<User>>
Result<Result<User>> wrapped = Result.Success(GetUser(id));  // FDDD008

// ✅ Use Bind to flatten
var user = someResult.Bind(id => GetUser(id));  // Result<User>

FDDD009: Blocking on Async Results

Problem: Blocking on Task<Result<T>> can cause deadlocks.

// ❌ Blocking call - can deadlock
var user = GetUserAsync(id).Result;  // FDDD009

// ✅ Use await
var user = await GetUserAsync(id);

FDDD012: Use Result.Combine

Problem: Manual result checking is verbose and error-prone.

// ❌ Manual checking (stops at first error)
var emailResult = EmailAddress.TryCreate(input.Email);
if (emailResult.IsFailure) return emailResult.Error;
var nameResult = FirstName.TryCreate(input.Name);
if (nameResult.IsFailure) return nameResult.Error;

// ✅ Use Combine to collect ALL errors
var result = EmailAddress.TryCreate(input.Email)
    .Combine(FirstName.TryCreate(input.Name))
    .Bind((email, name) => User.Create(email, name));
// Returns all validation errors at once

Configuration

The analyzers run automatically. To suppress specific warnings, use standard .NET mechanisms:

In code:

#pragma warning disable FDDD003  // Suppress unsafe value access warning
var value = result.Value;
#pragma warning restore FDDD003

In .editorconfig:

[*.cs]
# Change severity of specific rules
dotnet_diagnostic.FDDD003.severity = suggestion  # Downgrade to suggestion
dotnet_diagnostic.FDDD002.severity = none        # Disable completely
dotnet_diagnostic.FDDD007.severity = warning     # Upgrade to warning

Suppress all FunctionalDDD analyzers:


<PropertyGroup>
  <NoWarn>$(NoWarn);FDDD001;FDDD002;FDDD003;FDDD004;FDDD005;FDDD006;FDDD007;FDDD008;FDDD009;FDDD010;FDDD011;FDDD012;FDDD013;FDDD014;FDDD015;FDDD016;FDDD017;FDDD018</NoWarn>
</PropertyGroup>

Analyzer Rules

Rule ID Title Severity
FDDD001 Result return value is not handled Warning
FDDD002 Use Bind instead of Map when lambda returns Result Info
FDDD003 Unsafe access to Result.Value Warning
FDDD004 Unsafe access to Result.Error Warning
FDDD005 Consider using MatchError for error type discrimination Info
FDDD006 Unsafe access to Maybe.Value Warning
FDDD007 Use Create instead of TryCreate().Value Warning
FDDD008 Result is double-wrapped (Result<Result<T>>) Warning
FDDD009 Incorrect async Result usage (blocking instead of awaiting) Warning
FDDD010 Use specific error type instead of base Error class Info
FDDD011 Maybe is double-wrapped (Maybe<Maybe<T>>) Warning
FDDD012 Consider using Result.Combine Info
FDDD013 Consider using GetValueOrDefault or Match instead of ternary Info
FDDD014 Use async method variant for async lambda Warning
FDDD015 Don't throw exceptions in Result chains Warning
FDDD016 Error message should not be empty Warning
FDDD017 Don't compare Result or Maybe to null Warning
FDDD018 Unsafe access to Value in LINQ expression Warning

Requirements

  • .NET Standard 2.0 or later
  • Roslyn 4.0+ (Visual Studio 2022 or later, or .NET 6+ SDK)

Feedback

Found a false positive or have suggestions? Open an issue on GitHub.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

  • .NETStandard 2.0

    • 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
3.0.0-alpha.60 31 2/4/2026
3.0.0-alpha.59 36 2/1/2026
3.0.0-alpha.56 39 1/31/2026
3.0.0-alpha.55 41 1/31/2026