dotnet / roslyn-analyzers

MIT License
1.6k stars 468 forks source link

CA1416 in Wpf projects #4699

Open Rand-Random opened 3 years ago

Rand-Random commented 3 years ago

Edit: Demo Application: https://github.com/dotnet/roslyn-analyzers/files/5869822/WpfApp1.zip

Provided by @buyaa-n from here https://github.com/dotnet/roslyn-analyzers/issues/4699#issuecomment-767135598


Analyzer

Diagnostic ID: Severity Code Description Project File Line Suppression State Warning CA1416 'Utils.Foo4(object, string, BindingMode, IValueConverter, object, UpdateSourceTrigger, string)' is supported on 'Windows' 7.0 and later WpfApp12 (net5.0-windows) C:\Users\rfa\source\repos\WpfApp12\WpfApp12\Class1.cs 15 Active

Analyzer source

SDK: Built in Version: 5.0

Describe the bug

All methods of a referenced WPF library can trigger the warning CA1416, by double clicking the warning in error list the warning sometimes disappear from it self.

Steps To Reproduce

I have the following project layout

WpfApp1.sln
    ClassLibrary.csproj
    WpfLibrary.csproj
        References: ClassLibrary.csproj
    WpfApp1.csproj
        References: WpfLibrary.csproj, ClassLibrary.csproj

The csproj files look like this:

ClassLibrary.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net47;net5.0</TargetFrameworks>
  </PropertyGroup>
</Project>

WpfLibrary

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
  <PropertyGroup>
    <TargetFrameworks>net47;net5.0-windows</TargetFrameworks>
    <UseWPF>true</UseWPF>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj" />
  </ItemGroup>
</Project>

WpfApp1

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFrameworks>net47;net5.0-windows</TargetFrameworks>
    <UseWPF>true</UseWPF>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj" />
    <ProjectReference Include="..\WpfLibrary1\WpfLibrary1.csproj" />
  </ItemGroup>
</Project>

In WpfLibrary.csproj I have the following class:

public static class Utils
{
    private static bool? _isDesignMode;

    public static Binding CreateBinding(string path, BindingMode mode = BindingMode.TwoWay, IValueConverter converter = null, object parameter = null, UpdateSourceTrigger trigger = UpdateSourceTrigger.PropertyChanged, string stringFormat = "")
    {
        return CreateBinding(DependencyProperty.UnsetValue, path, mode, converter, parameter, trigger, stringFormat);
    }
    public static Binding CreateBinding(object sourceObject, string path, BindingMode mode = BindingMode.TwoWay, IValueConverter converter = null, object parameter = null, UpdateSourceTrigger trigger = UpdateSourceTrigger.PropertyChanged, string stringFormat = "")
    {
        return new Binding { Source = sourceObject, Path = new PropertyPath(path), Mode = mode, ConverterParameter = parameter, Converter = converter, UpdateSourceTrigger = trigger, StringFormat = stringFormat };
    }
    public static Binding Foo1(object sourceObject, string path, BindingMode mode = BindingMode.TwoWay, IValueConverter converter = null, object parameter = null, UpdateSourceTrigger trigger = UpdateSourceTrigger.PropertyChanged, string stringFormat = "")
    {
        return new Binding { Source = sourceObject, Path = new PropertyPath(path), Mode = mode, ConverterParameter = parameter, Converter = converter, UpdateSourceTrigger = trigger, StringFormat = stringFormat };
    }
    public static Binding Foo2(object sourceObject, string path, BindingMode mode = BindingMode.TwoWay, IValueConverter converter = null, object parameter = null, UpdateSourceTrigger trigger = UpdateSourceTrigger.PropertyChanged, string stringFormat = "")
    {
        return new Binding { Source = sourceObject, Path = new PropertyPath(path), Mode = mode, ConverterParameter = parameter, Converter = converter, UpdateSourceTrigger = trigger, StringFormat = stringFormat };
    }
    public static Binding Foo3(object sourceObject, string path, BindingMode mode = BindingMode.TwoWay, IValueConverter converter = null, object parameter = null, UpdateSourceTrigger trigger = UpdateSourceTrigger.PropertyChanged, string stringFormat = "")
    {
        return new Binding { Source = sourceObject, Path = new PropertyPath(path), Mode = mode, ConverterParameter = parameter, Converter = converter, UpdateSourceTrigger = trigger, StringFormat = stringFormat };
    }
    public static Binding Foo4(object sourceObject, string path, BindingMode mode = BindingMode.TwoWay, IValueConverter converter = null, object parameter = null, UpdateSourceTrigger trigger = UpdateSourceTrigger.PropertyChanged, string stringFormat = "")
    {
        return new Binding { Source = sourceObject, Path = new PropertyPath(path), Mode = mode, ConverterParameter = parameter, Converter = converter, UpdateSourceTrigger = trigger, StringFormat = stringFormat };
    }
    public static Binding Foo5(object sourceObject, string path, BindingMode mode = BindingMode.TwoWay, IValueConverter converter = null, object parameter = null, UpdateSourceTrigger trigger = UpdateSourceTrigger.PropertyChanged, string stringFormat = "")
    {
        return new Binding { Source = sourceObject, Path = new PropertyPath(path), Mode = mode, ConverterParameter = parameter, Converter = converter, UpdateSourceTrigger = trigger, StringFormat = stringFormat };
    }
    public static Binding Foo6(object sourceObject, string path, BindingMode mode = BindingMode.TwoWay, IValueConverter converter = null, object parameter = null, UpdateSourceTrigger trigger = UpdateSourceTrigger.PropertyChanged, string stringFormat = "")
    {
        return new Binding { Source = sourceObject, Path = new PropertyPath(path), Mode = mode, ConverterParameter = parameter, Converter = converter, UpdateSourceTrigger = trigger, StringFormat = stringFormat };
    }

    public static bool IsDesignMode()
    {
        if (_isDesignMode == null)
        {
            var descriptor = DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, typeof(FrameworkElement));
            _isDesignMode = (bool)descriptor.Metadata.DefaultValue;
        }

        return _isDesignMode.GetValueOrDefault(false);
    }
}

In WpfApp1.csproj in the App.xaml.cs I have the following code:

    public partial class App : Application
    {
        public App()
        {
            _ = Utils.CreateBinding("Foo", BindingMode.Default);
            _ = Utils.Foo1(null, "Foo", BindingMode.Default);
            _ = Utils.Foo2(null, "Foo", BindingMode.Default);
            _ = Utils.Foo3(null, "Foo", BindingMode.Default);

            _ = Utils.IsDesignMode();
        }
    }

Changing the code and/or rebuiding, it may help to make an error to let the build fail, adding more WPF dependent methods to Utils.cs and using it in App.xaml.cs, at some point of time you should get those warnings.

image Notice I spammed the methods from Utils.cs to other places of the application aswell, thats why you are seeing MainWindow.xaml.cs and Class1.cs aswell.

In my real application I have 500 warnings that block the view to actually warnings/errors. image

and I don't need to make changes, after rebuild of my solution will trigger those warnings.

Expected behavior

No warnings, or if those errors are actually valid they shouldn't disappear by clicking on them.

Actual behavior

Warnings.

Additional context

No more context to share.

Youssef1313 commented 3 years ago

@buyaa-n, The image shows warnings reported when the TFM is net5.0-windows

buyaa-n commented 3 years ago

@Rand-Random, it would be helpful if you could attach/share this sample project, i am not familiar with WPF so it would take time to wire them up myself, thanks.

buyaa-n commented 3 years ago

@Rand-Random I was not able to repro the warnings you are seeing after creating a solution with projects same as in your description. Building from VS or console, adding a new method into Utils.cs and using them in App.xaml.cs not triggering any warning, i might miss something you didn't mention, here is my sample project WpfApp1.zip

Anyway from your warnings image, I suspect you might be using a custom AssemblyInfo.cs file for your project instead of the one generated by SDK, if that is the case try add assembly level SupportedOSPlatform attribute in you custom AssemblyInfo.cs file as shown below: [assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("Windows7.0")]

Rand-Random commented 3 years ago

@buyaa-n I am facing the error with your application, hope this video helps to demonstrate it: https://user-images.githubusercontent.com/9868626/105822732-b039cb00-5fbc-11eb-8094-893c8096a375.mp4

buyaa-n commented 3 years ago

Thank you @Rand-Random i was able to repro, will investigate

buyaa-n commented 3 years ago

It seems VS Intellisense issue not the analyzer, it is not happening after build, only after rebuilding errornous code and fixing the errors. @mavasani could we transfer this issue to VS Intellisense team?

mavasani commented 3 years ago

@buyaa-n Can you please upload a zip file with the project where you can repro the issue? That would make it much easier for the next person to investigate to get the investigations going fast.

mavasani commented 3 years ago

@buyaa-n @Rand-Random Can you folks also mention the Visual Studio version where you are seeing the issue?

Youssef1313 commented 3 years ago

@mavasani It's already in https://github.com/dotnet/roslyn-analyzers/issues/4699#issuecomment-767135598

buyaa-n commented 3 years ago

@buyaa-n Can you please upload a zip file with the project where you can repro the issue? That would make it much easier for the next person to investigate to get the investigations going fast.

Yes, I have uploaded above, thanks @Youssef1313 , FYI it only reproducible after doing certain steps showed in the @Rand-Random 's recording

buyaa-n commented 3 years ago

@buyaa-n @Rand-Random Can you folks also mention the Visual Studio version where you are seeing the issue?

My VS is: Visual Studio Enterprise 2019 Preview, Version 16.9.0 Preview 2.0

mavasani commented 3 years ago

Perfect, thanks @buyaa-n. I'll investigate.

Youssef1313 commented 3 years ago

I just looked at the attached zip. I found <TargetFrameworks>net47;net5.0-windows</TargetFrameworks> in WpfApp1.csproj. Isn't the diagnostic correct in this case?

buyaa-n commented 3 years ago

I just looked at the attached zip. I found net47;net5.0-windows in WpfApp1.csproj. Isn't the diagnostic correct in this case?

Not correct, the analyzer should be off for net47, anyway i can repro it even without net47, i.e. only with <TargetFrameworks>net5.0-windows</TargetFrameworks>.

Youssef1313 commented 3 years ago

I'm not sure whether this is related or not. I have met a problem while working with RoslynAnalyzers.sln before, where a ton of diagnostics appeared unexpectedly. They went away on their own (See https://github.com/dotnet/roslyn-analyzers/pull/4720#discussion_r561201330). I've just encountered the same problem now (but there is no clear repro steps)

image

The diagnostics are shown from IntelliSense only. Note: The 2 errors in the above screenshot are from another branch that I was working on yesterday 😄 (I haven't closed Visual Studio - but those errors disappeared when I switched from that branch, and they're back again on their own).

Note 2: Most of the warnings are already suppressed in editorconfig:

https://github.com/dotnet/roslyn-analyzers/blob/c72e1d6b634d2467a8026a0ab8ee7ba3ff7097bc/.editorconfig#L205-L206

mavasani commented 3 years ago

This is same as https://github.com/dotnet/roslyn/issues/49718 and I have also seen this in the past. @Youssef1313 repro steps to get this reproing consistently would be great. I think it normally happens after an explicit build, but I could not get consistent repro.

Rand-Random commented 3 years ago

@buyaa-n You mentioned

could we transfer this issue to VS Intellisense team?

Should I create the issue at some other place, or are we good here?

@Youssef1313 @buyaa-n If I am not misunderstanding you, you are claming the Warning would be correct for .net47. Could you explain why, I don't see why this should be the case?


Would be great to see a fix in the near future as in my real project which is way bigger than the demonstrated case those warnings block the sight of the real issues, and because they like to appear/disappear when I switch between files I again and again lose the focus of the warnings I am actually trying to solve, so a rather annoying behaviour.

Youssef1313 commented 3 years ago

@Youssef1313 @buyaa-n If I am not misunderstanding you, you are claming the Warning would be correct for .net47. Could you explain why, I don't see why this should be the case?

I was incorrectly saying that. Sorry for any confusion.

buyaa-n commented 3 years ago

Should I create the issue at some other place, or are we good here?

No need, I think @mavasani is looking into this issue, he might give an update when he is ready

RussKie commented 3 years ago

I get the same for Windows Forms

Project file

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Library</OutputType>
    <TargetFramework>net5.0-windows</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <SupportedPlatform Include="windows7.0" />
  </ItemGroup>

Properties\AssemblyInfo.cs

[assembly: SupportedOSPlatform("windows")]

And yet I get these errors: image

buyaa-n commented 3 years ago

@RussKie your issue doesn't seem to related to this issue.

The problem is your app is referencing APIs only supported from windows 7.0 but targeting unversioned windows (lower version). This means users of your app can use your app on windows lower than 7.0 where the referenced APIs are not supported. So there can be 2 solutions:

  1. You can change your app to target Windows 7.0 or above <TargetFramework>net5.0-windows7.0</TargetFramework>
  2. Or if the referencing APIs are supported on lower versions than 7.0 and you have control over the APIs then lower the version of referencing APIs

FYI windows is already in the MSBuild SupportedPlatforms list no need to add this manually, i.e. below setting has no effect

  <ItemGroup>
    <SupportedPlatform Include="windows7.0" />
  </ItemGroup>
RussKie commented 3 years ago

.NET 5.0 does not support anything below Windows 7 SP1, so I'm confused how my API can be supported below that.

buyaa-n commented 3 years ago

Right, but that does not really matter the for analyzer, version less platform means any version (which is Version 0.0 so it is lower than 7.0)

RussKie commented 3 years ago

@buyaa-n it doesn't help.

sygmond commented 3 years ago

In my case I was getting the warnings because I'm using AssemblyInfo.cs with <GenerateAssemblyInfo>false</GenerateAssemblyInfo> in Project.cspropj. I need it this way because I'm managing app versioning via git merge commits.

To resolve the warnings, this worked for me:

In AssemblyInfo.cs add the line:

[assembly: SupportedOSPlatform("windows7.0")]

Note: If you add "windows" instead of "windows7.0", the CA1416 warning will come back, because the NET 5 doesn't support all windows platform but only windows-7.0 and above. If you want to use it only for windows 10, use [assembly: SupportedOSPlatform("windows10.0")]. Using "windows10" or "windows7" will not work, you need to add .0 at the end.

Thank you all for the valuable info!

RussKie commented 3 years ago

Thank you @sygmond, I'll give it a go.

RussKie commented 3 years ago

@sygmond thank you, your suggestion worked!

sygmond commented 3 years ago

@sygmond thank you, your suggestion worked!

Glad that it worked. 99% of the work was done by people that commented above and 1% my insight. Its great when we put our heads together and get good results.