- versions used: SonarCloud & SonarLint, C#, Visual Studio 2019
- Rule affected: csharpsquid:S2259 “’‘providerCourses’ is null on at least one execution path.”
- minimal code sample to reproduce (with analysis parameter, and potential instructions to compile).
Combining a null-coalescing operator with the “continue” keyword in a for loop still reports the variable usage with is null on at least one execution path
Sample 1, no loop + null coalescing operator:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sample1
{
class Sample1
{
static void Main(string[] args)
{
var providerCourse = new ProviderCourse
{
Items = new List<string>
{
"item1",
"item2"
}
};
if (!providerCourse?.Items?.Any() ?? true)
{
Console.WriteLine("FAIL");
throw new InvalidOperationException();
}
// Here providerCourse is not reported by SonarCloud/SonarLint
var _ = providerCourse.Items.Where(items => items == "item1");
}
}
public class ProviderCourse
{
public IEnumerable<string> Items { get; set; }
}
}
Sample 2, loop with null || !.any():
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sample2
{
class Sample2
{
static void Main(string[] args)
{
var providerCourses = new List<ProviderCourse>
{
new ProviderCourse
{
Items = new List<string>
{
"item1",
"item2"
}
},
new ProviderCourse
{
Items = new List<string>
{
"item1",
"item2"
}
}
};
foreach (var providerCourse in providerCourses)
{
if (providerCourse?.Items == null || !providerCourse.Items.Any())
{
Console.WriteLine("FAIL");
continue;
}
// Here providerCourse is not reported by SonarCloud/SonarLint
var _ = providerCourse.Items.Where(items => items == "item1");
}
}
}
public class ProviderCourse
{
public IEnumerable<string> Items { get; set; }
}
}
Sample 3, loop + combine null and !.any() check:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sample3
{
class Sample3
{
static void Main(string[] args)
{
var providerCourses = new List<ProviderCourse>
{
new ProviderCourse
{
Items = new List<string>
{
"item1",
"item2"
}
},
new ProviderCourse
{
Items = new List<string>
{
"item1",
"item2"
}
}
};
foreach (var providerCourse in providerCourses)
{
if (!providerCourse?.Items?.Any() ?? true)
{
Console.WriteLine("FAIL");
continue;
}
// Here providerCourse is reported with "‘providerCourses’ is null on at least one execution path."
// While behaving exactly like Sample 2.
var _ = providerCourse.Items.Where(items => items == "item1");
}
}
}
public class ProviderCourse
{
public IEnumerable<string> Items { get; set; }
}
}
In case of Sample3 “providerCourse” can’t ever be null, and should not be reported as such.