The CBDE rule S3949 is throwing when run inside VS2015 or building using MSBuild if only .NET Framework 4.6.1 is installed:


The rule is not expected to be run in VS or a "normal" MSBuild build (i.e. should not run unless running under the _Scanner for MSBuild_).
However, some of the code in the rule has to run to do the "should I run?" detection. The line that is causing the problem seems to be the call to RuntimeInformation.IsOSPlatform (it's defined in the assembly that can't be found).
protected sealed override void Initialize(SonarAnalysisContext context)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
new CbdeHandler(context, OnCbdeIssue, ShouldRunCbdeInContext, () => { return WorkDirectoryBasePath; },
testCbdeBinaryPath, onCbdeExecution);
}
}
It looks like that API first shipped in .NET Framework v4.7.1 (see apisof.net)
The issue was discovered when testing a pre-release version of SLVS that embedded SonarC# v8.2, but it can easily be reproduced just be referencing the analyzer NuGet without SLVS being installed.
CBDE rule does not produce an error or return any issues.
CBDE rule throws AD0001
Warning AD0001 Analyzer 'SonarAnalyzer.Rules.CSharp.CbdeHandlerRule' threw an exception of type 'System.IO.FileNotFoundException' with message 'Could not load file or assembly 'System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.'.
Manually disable the rule in a ruleset.
Or do nothing. There are no adverse side-effects on VS or the other analyzer rules, other than creating a poor impression for the user.
When developing this part, we first tried to use Environment.OSVersion.Platform, but this does not work with .NET core. This is why we moved to the more recent RuntimeInformation.IsOSPlatform, which does not work with old .NET framework. Like for the issue with parsing JSON, having one code targeting several runtime environments is the root cause of the issue...
Maybe a way to solve this in this case would be to first detect which version of the .NET framework we run with, and if the answer is .NET framework 4.6, it implies we are on windows, otherwise, execute the current code?
https://stackoverflow.com/a/35889259/2090515 to detect version of .NET
I don't agree about the root cause, but a proper root cause analysis is out of scope for the current issue and better suited to an offline discussion.
... otherwise, execute the current code?
I don't think the problem occurs when the code is executed; I think it will occur before that when the JIT compiler attempts to JIT it. You could try to structure the code in such a way that the JIT compiler won't attempt to JIT that specific line of code, but that's hacky and unreliable. It would be better to find a solution that uses only APIs in the current supported .NET version.
I'm assuming this is a relatively short-term problem since non-Windows operating systems will be supported at some point. Also, I'm assuming we don't care which version of Windows or .NET is running - we only want to know if the current OS is Windows or not. (<-- true for fixing this specific bug, but we'll need some way of detecting the other platforms in the future).
What is the behaviour of Environment.OSVersion.Platform on .NET Core? Does it throw, and if so, on which platforms - all, or only some? If it works on Windows and throws on other platforms then that would be good enough for our current purposes.
Another option would be to check for the presence of well-known environment variables that are Windows-specific or contain Windows-specific values e.g. windir or os. This is hacky, but more predictable than playing tricks with the JIT compiler.
We could also try a combination of approaches e.g. try Environment.OSVersion.Platform, catch any exceptions, and fall back on checking environment variables.
https://www.nuget.org/packages/Microsoft.DotNet.PlatformAbstractions/ looks worth investigating. It works with .NET 4.5.1+ and has a RuntimeEnvironment.OperatingSystem property.
When developing this part, we first tried to use Environment.OSVersion.Platform, but this does not work with .NET core.
@loic-joly-sonarsource: Do you remember what was the problem you encountered using Environment.OSVersion.Platform on .Net Core? According to docs, it should be supported starting with .Net Core 2: https://docs.microsoft.com/en-us/dotnet/api/system.environment.osversion?view=netframework-4.8
Most helpful comment
https://www.nuget.org/packages/Microsoft.DotNet.PlatformAbstractions/ looks worth investigating. It works with .NET 4.5.1+ and has a
RuntimeEnvironment.OperatingSystemproperty.