S3236 reports on caller information being hided which is inconvenient/fp?!

If we take a look to the code below:

using System;
using System.Runtime.CompilerServices;

namespace S3236;

public class MyModel
{
    public MyModel(int? total = null)
    {
		NotNegative(total ?? 0, nameof(total)); // Inconvenient report
		Total = total;
    }

	public int? Total { get; init; }

	private static int NotNegative(int parameter, [CallerArgumentExpression(nameof(parameter))] string? paramName = null)
	=> parameter < 0
		? throw new ArgumentOutOfRangeException(paramName, "Value should not be negative.")
		: parameter;
}

The NotNegative check is only implemented for int and not for int?. To be able to test the constraint, the ?? 0 is passed, so that default<int> also succeeds. However, S3236 reports on caller information being dropped. Although true, you could argue that given the constraints this is the cleanest solution:

  1. Having an overload NotNegative(int? parameter) could potentially return int and guard to null too (that would at least be consistent with other methods in the actual Guard class).
  2. if(total is not null) NotNegative(total); (with or without nesting and/or brackets is not easier to read.
  3. Having a parameter name "total ?? 0" is not the right parameter to call on.
  4. Using any form of suppression makes things worse (from a readability perspective)

So, I think I would like to see an exception made when a nameof() is used, and in the expression that nameof() is used. I see the risk of introducing false negatives, but I would guess that likeliness is low.

Note, to simplify the reproduction, this case has been simplified. The guarding method lives somewhere else in my case.

Reported by SonarAnalyzer.CSharp v10.29.0.143774

Hello, thank you for feedback! I’ve created a JIRA ticket to investigate this FP.