diff --git a/.gitignore b/.gitignore index c574ce6112e..f6e7125061f 100644 --- a/.gitignore +++ b/.gitignore @@ -374,3 +374,4 @@ Temporary Items # Ignore "InteropTests/NativeTests/out" InteropTests/NativeTests/out /src/System.Windows.Forms/src/comctl32.dll +*.received.* diff --git a/Directory.Build.props b/Directory.Build.props index 5e526a38f57..9e43a25628b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - + @@ -17,6 +17,11 @@ true + + + Calibri, 11pt, style=regular + + false diff --git a/Winforms.sln b/Winforms.sln index 3c29397fe7f..53bc60ef656 100644 --- a/Winforms.sln +++ b/Winforms.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 -VisualStudioVersion = 16.0.28627.84 +VisualStudioVersion = 16.0.31329.18 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinformsControlsTest", "src\System.Windows.Forms\tests\IntegrationTests\WinformsControlsTest\WinformsControlsTest.csproj", "{657472B8-FDA3-49DF-B8BE-0246046A4348}" EndProject @@ -84,7 +84,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "documentation", "documentat docs\winforms-designer.md = docs\winforms-designer.md EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "accessibility", "accessibility", "{D390F7D2-1E11-4DEE-B7F1-4FD0681A81F0}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Accessibility", "Accessibility", "{D390F7D2-1E11-4DEE-B7F1-4FD0681A81F0}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccessibilityTests", "src\System.Windows.Forms\tests\AccessibilityTests\AccessibilityTests.csproj", "{A9F13504-5560-4CC7-80A2-11E23E5852E1}" EndProject @@ -163,6 +163,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MauiScrollBarTests", "src\S EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MauiMDITests", "src\System.Windows.Forms\tests\IntegrationTests\MauiTests\MauiMDITests\MauiMDITests.csproj", "{5D8C7F71-8B58-48B4-8A54-11E5AB8B02CA}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Windows.Forms.Analyzers", "src\System.Windows.Forms.Analyzers\src\System.Windows.Forms.Analyzers.csproj", "{3596BDE6-B211-4BE7-810D-DC7A4315E296}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Windows.Forms.Analyzers.Tests", "src\System.Windows.Forms.Analyzers\tests\UnitTests\System.Windows.Forms.Analyzers.Tests.csproj", "{E742382E-5D34-481D-A3FC-7B6B9C5033E2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzers", "Analyzers", "{E4C6C5F5-46E9-4C63-9628-26752B4D9C11}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzers", "Analyzers", "{D3AD0BF9-F5E2-4913-9AE3-9C4998F95EA1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Windows.Forms.Analyzers.CSharp", "src\System.Windows.Forms.Analyzers.CSharp\src\System.Windows.Forms.Analyzers.CSharp.csproj", "{5025D7FF-EB6D-4250-B9C5-887ADFBBD952}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Windows.Forms.Analyzers.CSharp.Tests", "src\System.Windows.Forms.Analyzers.CSharp\tests\UnitTests\System.Windows.Forms.Analyzers.CSharp.Tests.csproj", "{714EC82C-D4E0-4E14-9C34-4F270774D384}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -719,16 +731,16 @@ Global {83634671-CF3A-43B0-B729-42CCBA62DF2C}.Release|x64.Build.0 = Release|Any CPU {83634671-CF3A-43B0-B729-42CCBA62DF2C}.Release|x86.ActiveCfg = Release|Any CPU {83634671-CF3A-43B0-B729-42CCBA62DF2C}.Release|x86.Build.0 = Release|Any CPU - {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|Any CPU.ActiveCfg = Debug|x64 - {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|Any CPU.Build.0 = Debug|x64 + {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|Any CPU.ActiveCfg = Debug|x86 + {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|Any CPU.Build.0 = Debug|x86 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|arm64.ActiveCfg = Debug|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|arm64.Build.0 = Debug|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|x64.ActiveCfg = Debug|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|x64.Build.0 = Debug|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|x86.ActiveCfg = Debug|x86 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Debug|x86.Build.0 = Debug|x86 - {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|Any CPU.ActiveCfg = Release|x64 - {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|Any CPU.Build.0 = Release|x64 + {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|Any CPU.ActiveCfg = Release|x86 + {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|Any CPU.Build.0 = Release|x86 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|arm64.ActiveCfg = Release|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|arm64.Build.0 = Release|x64 {C272DA06-B98D-4BB7-B1C4-ECF58F54B224}.Release|x64.ActiveCfg = Release|x64 @@ -943,6 +955,70 @@ Global {5D8C7F71-8B58-48B4-8A54-11E5AB8B02CA}.Release|x64.Build.0 = Release|Any CPU {5D8C7F71-8B58-48B4-8A54-11E5AB8B02CA}.Release|x86.ActiveCfg = Release|Any CPU {5D8C7F71-8B58-48B4-8A54-11E5AB8B02CA}.Release|x86.Build.0 = Release|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Debug|arm64.ActiveCfg = Debug|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Debug|arm64.Build.0 = Debug|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Debug|x64.ActiveCfg = Debug|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Debug|x64.Build.0 = Debug|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Debug|x86.ActiveCfg = Debug|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Debug|x86.Build.0 = Debug|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Release|Any CPU.Build.0 = Release|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Release|arm64.ActiveCfg = Release|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Release|arm64.Build.0 = Release|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Release|x64.ActiveCfg = Release|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Release|x64.Build.0 = Release|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Release|x86.ActiveCfg = Release|Any CPU + {3596BDE6-B211-4BE7-810D-DC7A4315E296}.Release|x86.Build.0 = Release|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Debug|arm64.ActiveCfg = Debug|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Debug|arm64.Build.0 = Debug|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Debug|x64.ActiveCfg = Debug|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Debug|x64.Build.0 = Debug|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Debug|x86.ActiveCfg = Debug|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Debug|x86.Build.0 = Debug|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Release|Any CPU.Build.0 = Release|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Release|arm64.ActiveCfg = Release|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Release|arm64.Build.0 = Release|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Release|x64.ActiveCfg = Release|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Release|x64.Build.0 = Release|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Release|x86.ActiveCfg = Release|Any CPU + {E742382E-5D34-481D-A3FC-7B6B9C5033E2}.Release|x86.Build.0 = Release|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Debug|arm64.ActiveCfg = Debug|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Debug|arm64.Build.0 = Debug|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Debug|x64.ActiveCfg = Debug|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Debug|x64.Build.0 = Debug|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Debug|x86.ActiveCfg = Debug|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Debug|x86.Build.0 = Debug|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Release|Any CPU.Build.0 = Release|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Release|arm64.ActiveCfg = Release|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Release|arm64.Build.0 = Release|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Release|x64.ActiveCfg = Release|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Release|x64.Build.0 = Release|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Release|x86.ActiveCfg = Release|Any CPU + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952}.Release|x86.Build.0 = Release|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Debug|Any CPU.Build.0 = Debug|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Debug|arm64.ActiveCfg = Debug|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Debug|arm64.Build.0 = Debug|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Debug|x64.ActiveCfg = Debug|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Debug|x64.Build.0 = Debug|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Debug|x86.ActiveCfg = Debug|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Debug|x86.Build.0 = Debug|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Release|Any CPU.ActiveCfg = Release|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Release|Any CPU.Build.0 = Release|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Release|arm64.ActiveCfg = Release|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Release|arm64.Build.0 = Release|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Release|x64.ActiveCfg = Release|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Release|x64.Build.0 = Release|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Release|x86.ActiveCfg = Release|Any CPU + {714EC82C-D4E0-4E14-9C34-4F270774D384}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1004,6 +1080,12 @@ Global {05FD23CE-60AE-44A8-8DD6-1688F04BE385} = {DF68A171-D27B-4E6A-8A7E-63A651622355} {872E41E1-FF66-4B12-A273-1F26A548666F} = {8F20A905-BD37-4D80-B8DF-FA45276FC23F} {5D8C7F71-8B58-48B4-8A54-11E5AB8B02CA} = {8F20A905-BD37-4D80-B8DF-FA45276FC23F} + {3596BDE6-B211-4BE7-810D-DC7A4315E296} = {E4C6C5F5-46E9-4C63-9628-26752B4D9C11} + {E742382E-5D34-481D-A3FC-7B6B9C5033E2} = {D3AD0BF9-F5E2-4913-9AE3-9C4998F95EA1} + {E4C6C5F5-46E9-4C63-9628-26752B4D9C11} = {77FEDB47-F7F6-490D-AF7C-ABB4A9E0B9D7} + {D3AD0BF9-F5E2-4913-9AE3-9C4998F95EA1} = {DF68A171-D27B-4E6A-8A7E-63A651622355} + {5025D7FF-EB6D-4250-B9C5-887ADFBBD952} = {E4C6C5F5-46E9-4C63-9628-26752B4D9C11} + {714EC82C-D4E0-4E14-9C34-4F270774D384} = {D3AD0BF9-F5E2-4913-9AE3-9C4998F95EA1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7B1B0433-F612-4E5A-BE7E-FCF5B9F6E136} diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4b55af7d354..5b8671d4a91 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,6 +6,7 @@ trigger: - main - release/* - internal/release/* +- internal/experimental/* # trigger ci builds on pull requests into main and any release branches pr: diff --git a/eng/Localize/LocProject.json b/eng/Localize/LocProject.json index dfe4e1bdd07..fd5c9688455 100644 --- a/eng/Localize/LocProject.json +++ b/eng/Localize/LocProject.json @@ -77,6 +77,16 @@ "CopyOption": "LangIDOnName", "OutputPath": ".\\src\\System.Windows.Forms\\tests\\IntegrationTests\\WinformsControlsTest\\xlf\\" }, + { + "SourceFile": ".\\src\\System.Windows.Forms.Analyzers\\src\\Resources\\xlf\\SR.xlf", + "CopyOption": "LangIDOnName", + "OutputPath": ".\\src\\System.Windows.Forms.Analyzers\\src\\Resources\\xlf\\" + }, + { + "SourceFile": ".\\src\\System.Windows.Forms.Analyzers.CSharp\\src\\Resources\\xlf\\SR.xlf", + "CopyOption": "LangIDOnName", + "OutputPath": ".\\src\\System.Windows.Forms.Analyzers.CSharp\\src\\Resources\\xlf\\" + }, { "SourceFile": ".\\src\\System.Windows.Forms.Design\\src\\Resources\\System\\ComponentModel\\Design\\xlf\\BinaryEditor.xlf", "CopyOption": "LangIDOnName", diff --git a/eng/Versions.props b/eng/Versions.props index 8778e0ed1db..455bad30af7 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -56,10 +56,17 @@ 2.4.1 $(XUnitVersion) $(XUnitVersion) - $(XUnitVersion) + 2.4.3 $(XUnitVersion) 1.0.33-beta + + + 11.18.2 + + 6.0.0-preview.4.21253.7 + 4.10.0 + 2.7.0 @@ -70,11 +77,12 @@ 3.3.2 4.0.0-1.final + 4.0.0-1.final + 1.0.1-beta1.21265.1 3.3.0-beta1.final - 4.10.0 1.0.0 3.0.0 9.0.1 diff --git a/eng/packageContent.targets b/eng/packageContent.targets index ebf42c37cac..0d2a30a1fdf 100644 --- a/eng/packageContent.targets +++ b/eng/packageContent.targets @@ -10,9 +10,18 @@ true + + + <_AnalyzerTargetLanguage>$(AssemblyName.Substring($(AssemblyName.LastIndexOf('.')))) + /cs + /vb + + lib/$(TargetFramework) lib/$(TargetFramework);$(RefPackagePath) + + sdk/analyzers/dotnet$(AnalyzerTargetLanguage) @@ -30,7 +39,7 @@ $([System.IO.Path]::ChangeExtension('$(TargetRefPath)', '.xml')) $([System.IO.Path]::ChangeExtension('$(TargetPath)', '.xml')) - $([System.IO.Path]::GetDirectoryName('$(IntellisenseXml)')) + $([System.IO.Path]::GetDirectoryName('$(IntellisenseXmlDest)')) @@ -39,7 +48,7 @@ This means we build a real assembly that has no associated official intellisense docs. Contact the intellisense team for guidance. --> - diff --git a/pkg/Microsoft.Private.Winforms/Directory.Build.targets b/pkg/Microsoft.Private.Winforms/Directory.Build.targets index 2b04482ea39..f9ecdbc4fab 100644 --- a/pkg/Microsoft.Private.Winforms/Directory.Build.targets +++ b/pkg/Microsoft.Private.Winforms/Directory.Build.targets @@ -4,25 +4,28 @@ <_PowerShellExe Condition="'$(_PowerShellExe)' == ''">C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe - <_ScriptLocation Condition="'$(_ScriptLocation)' == ''">$(MSBuildProjectDirectory)\ProcessFrameworkListFiles.ps1 - <_ManifestFile>$(MSBuildProjectDirectory)\FrameworkListFiles.props + <_ScriptLocation Condition="'$(_ScriptLocation)' == ''">$(MSBuildProjectDirectory)\sdk\dotnet-windowsdesktop\UpdateFileClassification.ps1 + <_ManifestFile>$(MSBuildProjectDirectory)\sdk\dotnet-windowsdesktop\System.Windows.Forms.FileClassification.props false - <_NuspecFile Include="@(NuGetPackOutput)" Condition="'%(Extension)' == '.nuspec'" /> + + + - + diff --git a/pkg/Microsoft.Private.Winforms/Microsoft.Private.Winforms.csproj b/pkg/Microsoft.Private.Winforms/Microsoft.Private.Winforms.csproj index fa87dc0a12e..58ae9d6ab88 100644 --- a/pkg/Microsoft.Private.Winforms/Microsoft.Private.Winforms.csproj +++ b/pkg/Microsoft.Private.Winforms/Microsoft.Private.Winforms.csproj @@ -17,7 +17,7 @@ $(TargetFrameworkName)$(TargetFrameworkVersion) - $(NoWarn);NU5131;NU5128 + $(NoWarn);NU5100;NU5131;NU5128 @@ -52,14 +52,12 @@ + - + True - + sdk\dotnet-windowsdesktop - - - True sdk\dotnet-wpf @@ -98,6 +96,12 @@ + + + + diff --git a/pkg/Microsoft.Private.Winforms/README.md b/pkg/Microsoft.Private.Winforms/README.md index 3059b17f27b..ef6ab5b2acf 100644 --- a/pkg/Microsoft.Private.Winforms/README.md +++ b/pkg/Microsoft.Private.Winforms/README.md @@ -1,33 +1,53 @@ ## Overview -This is a transport package consumed by [WPF](https://github.com/dotnet/wpf/) and [WindowsDesktop](https://github.com/dotnet/windowsdesktop/). +This is a transport package consumed by [WPF](https://github.com/dotnet/wpf/) and [WindowsDesktop](https://github.com/dotnet/windowsdesktop/). Some packaging configurations are defined in this project, and others are defined in \\eng\packageContent.targets. -WindowsDesktop relies on [`FrameworkListFiles.props` manifest](FrameworkListFiles.props) to list all our assemblies that form "WindowsForms" SDK[¹](#ref1). -The props file is then imported by [WindowsDesktop projects](https://github.com/dotnet/windowsdesktop/blob/master/pkg/windowsdesktop/pkg/Directory.Build.props). +## `sdk\dotnet-windowsdesktop` folder + +This folder contains props and targets used to ingest our assemblies into the [Windows Desktop SDK](https://github.com/dotnet/windowsdesktop/) for purpose of bundling of our analyzers into Microsoft.WindowsDesktop.App.Ref pack. + +* [`System.Windows.Forms.FileClassification.props`](sdk\dotnet-windowsdesktop\System.Windows.Forms.FileClassification.props) contains a manifest for the "WindowsForms" SDK[¹](#ref1), i.e. a list of our assemblies that form it. +The file is imported by [Microsoft.WindowsDesktop.App.Ref project](https://github.com/dotnet/windowsdesktop/blob/main/pkg/windowsdesktop/sfx/Microsoft.WindowsDesktop.App.Ref.sfxproj).
The manifest will need to be rebuilt if there are changes in the solution with respect to the SDK assemblies, e.g. a new assembly is added or an existing assembly is removed. -## How it works +### How to update System.Windows.Forms.FileClassification.props -[`FrameworkListFiles.props`](FrameworkListFiles.props) will be compared against the list of assemblies in `Microsoft.Private.Winforms.[version].nuspec`[²](#ref2) generated as part of the `pack` command. +The existing `System.Windows.Forms.FileClassification.props` is be compared against the list of assemblies in `Microsoft.Private.Winforms.[version].nuspec`[²](#ref2) generated as part of the `pack` command. If the content of these files differ - the build will fail. :warning: The process is purposefully made manual to ensure changes in the manifest are made consciously. -## How to update the manifest +To update the manifest run the following command and check in the updated files manifest: + +``` +.\build.cmd -pack /p:GenerateManifest=true +``` + +To debug the script run the following command: + +``` +dotnet build .\pkg\Microsoft.Private.Winforms\Microsoft.Private.Winforms.csproj /t:UpdateTransportPackage /p:GenerateManifest=true /v:m /bl /p:CommonLibrary_NativeInstallDir=$env:UserProfile\.netcoreeng\native\ +``` + + + + +## `sdk\dotnet-wpf` folder + +This folder contains props and targets that are part of [Windows Desktop SDK](https://github.com/dotnet/wpf/blob/main/packaging/Microsoft.NET.Sdk.WindowsDesktop/) (which is hosted and assembled in [dotnet/wpf](https://github.com/dotnet/wpf/)). +These files are referenced the [Microsoft.NET.Sdk.WindowsDesktop project](https://github.com/dotnet/wpf/blob/main/packaging/Microsoft.NET.Sdk.WindowsDesktop/Microsoft.NET.Sdk.WindowsDesktop.ArchNeutral.csproj)'s props and targets located [here](https://github.com/dotnet/wpf/blob/main/packaging/Microsoft.NET.Sdk.WindowsDesktop/targets). When this project is being built, it copies the files from our transport NuGet package to a Microsoft.NET.Sdk.WindowsDesktop bundle. + +* [`Microsoft.NET.Sdk.WindowsDesktop.WindowsForms.props`](sdk\dotnet-wpf\Microsoft.NET.Sdk.WindowsDesktop.WindowsForms.props) contains various Windows Forms specific configurations, such as our default `using` imports. -* Build the solution as normal: - ``` - .\build.cmd - ``` -* To update the manifest [`FrameworkListFiles.props`](FrameworkListFiles.props) run the following command from a developer prompt: - ``` - msbuild .\pkg\Microsoft.Private.Winforms\Microsoft.Private.Winforms.csproj /t:UpdateTransportPackage /p:GenerateManifest=true /v:m - ``` +* [`Microsoft.NET.Sdk.WindowsDesktop.WindowsForms.targets`](sdk\dotnet-wpfp\Microsoft.NET.Sdk.WindowsDesktop.WindowsForms.targets) contains various Windows Forms specific targets. +* [`System.Windows.Forms.Analyzers.props`](sdk\dotnet-wpf\System.Windows.Forms.Analyzers.props) contains a list of properties required by our source generators. +
+
---- diff --git a/pkg/Microsoft.Private.Winforms/FrameworkListFiles.props b/pkg/Microsoft.Private.Winforms/sdk/dotnet-windowsdesktop/System.Windows.Forms.FileClassification.props similarity index 71% rename from pkg/Microsoft.Private.Winforms/FrameworkListFiles.props rename to pkg/Microsoft.Private.Winforms/sdk/dotnet-windowsdesktop/System.Windows.Forms.FileClassification.props index accd13f16a8..1b9051de847 100644 --- a/pkg/Microsoft.Private.Winforms/FrameworkListFiles.props +++ b/pkg/Microsoft.Private.Winforms/sdk/dotnet-windowsdesktop/System.Windows.Forms.FileClassification.props @@ -1,3 +1,7 @@ + @@ -6,6 +10,8 @@ + + diff --git a/pkg/Microsoft.Private.Winforms/ProcessFrameworkListFiles.ps1 b/pkg/Microsoft.Private.Winforms/sdk/dotnet-windowsdesktop/UpdateFileClassification.ps1 similarity index 78% rename from pkg/Microsoft.Private.Winforms/ProcessFrameworkListFiles.ps1 rename to pkg/Microsoft.Private.Winforms/sdk/dotnet-windowsdesktop/UpdateFileClassification.ps1 index c5b0471d56a..2d0e183338d 100644 --- a/pkg/Microsoft.Private.Winforms/ProcessFrameworkListFiles.ps1 +++ b/pkg/Microsoft.Private.Winforms/sdk/dotnet-windowsdesktop/UpdateFileClassification.ps1 @@ -9,24 +9,24 @@ Param( [Parameter(ValueFromRemainingArguments=$true)][String[]] $properties ) -$assemblies = @( ); -# this isn't explicitly present in the list -$assemblies += 'System.Drawing.Common.dll'; [xml] $xmlDoc = Get-Content -Path $NuspecFile -Force; -$xmlDoc.package.files.file | ` + +$assemblies = $xmlDoc.package.files.file | ` Where-Object { # take only assemblies placed in \lib\netcoreappX.Y, and that are not resources # also exclude Accessibility.dll as it is explicitly added to WindowsDesktop bundle - $_.target.StartsWith('lib\') ` + ($_.target.StartsWith('lib\') -or $_.target.StartsWith('ref\') -or $_.target.StartsWith('sdk\analyzers\'))` -and $_.target.EndsWith('.dll', [System.StringComparison]::OrdinalIgnoreCase) ` -and !$_.target.EndsWith('resources.dll', [System.StringComparison]::OrdinalIgnoreCase) ` -and !$_.target.EndsWith('\Accessibility.dll', [System.StringComparison]::OrdinalIgnoreCase) } | ` - ForEach-Object { - $assembly = Split-Path $_.target -Leaf; - $assemblies += $assembly; - }; + Select-Object -Unique @{Name="Path";Expression={Split-Path $_.target -Leaf}} | ` + Select-Object -ExpandProperty Path; + +# this isn't explicitly present in the list +$assemblies += 'System.Drawing.Common.dll'; + $needGenerate = $null; [bool]::TryParse($GenerateManifest, [ref]$needGenerate) | Out-Null; @@ -57,17 +57,20 @@ else { Update the existing manifest #> Write-Host "Regenerating the manifest" -ForegroundColor Green - $output = " - - "; + + $output = " + + `r`n"; $assemblies | ` Sort-Object | ` ForEach-Object { $assembly = $_; - $output += " - " + $output += " `r`n" } $output += " - "; +"; $output | Out-File -FilePath $TargetFile -Encoding utf8 -Force; } diff --git a/pkg/Microsoft.Private.Winforms/sdk/dotnet-wpf/Microsoft.NET.Sdk.WindowsDesktop.WindowsForms.props b/pkg/Microsoft.Private.Winforms/sdk/dotnet-wpf/Microsoft.NET.Sdk.WindowsDesktop.WindowsForms.props index 4f6bbf02328..6c022cc3207 100644 --- a/pkg/Microsoft.Private.Winforms/sdk/dotnet-wpf/Microsoft.NET.Sdk.WindowsDesktop.WindowsForms.props +++ b/pkg/Microsoft.Private.Winforms/sdk/dotnet-wpf/Microsoft.NET.Sdk.WindowsDesktop.WindowsForms.props @@ -24,4 +24,7 @@ + + + diff --git a/pkg/Microsoft.Private.Winforms/sdk/dotnet-wpf/System.Windows.Forms.Analyzers.props b/pkg/Microsoft.Private.Winforms/sdk/dotnet-wpf/System.Windows.Forms.Analyzers.props new file mode 100644 index 00000000000..6b92891c167 --- /dev/null +++ b/pkg/Microsoft.Private.Winforms/sdk/dotnet-wpf/System.Windows.Forms.Analyzers.props @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/System.Windows.Forms.Analyzers.CSharp/ApplicationConfigurationGenerator.Help.md b/src/System.Windows.Forms.Analyzers.CSharp/ApplicationConfigurationGenerator.Help.md new file mode 100644 index 00000000000..26ba76cdb31 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/ApplicationConfigurationGenerator.Help.md @@ -0,0 +1 @@ +# How to use ApplicationConfigurationGenerator diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/AnalyzerReleases.Shipped.md b/src/System.Windows.Forms.Analyzers.CSharp/src/AnalyzerReleases.Shipped.md new file mode 100644 index 00000000000..d567f14248e --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/AnalyzerReleases.Shipped.md @@ -0,0 +1,3 @@ +; Shipped analyzer releases +; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/AnalyzerReleases.Unshipped.md b/src/System.Windows.Forms.Analyzers.CSharp/src/AnalyzerReleases.Unshipped.md new file mode 100644 index 00000000000..628d4d2fbb0 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/AnalyzerReleases.Unshipped.md @@ -0,0 +1,8 @@ +; Unshipped analyzer release +; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +### New Rules +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +WFAC001 | ApplicationConfiguration | Error | ApplicationConfigurationGenerator, [Documentation](https://github.com/dotnet/winforms/blob/main/src/System.Windows.Forms.Analyzers.CSharp/ApplicationConfigurationGenerator.Help.md) +WFAC002 | ApplicationConfiguration | Error | ApplicationConfigurationGenerator, [Documentation](https://github.com/dotnet/winforms/blob/main/src/System.Windows.Forms.Analyzers.CSharp/ApplicationConfigurationGenerator.Help.md) diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/DiagnosticDescriptors.cs b/src/System.Windows.Forms.Analyzers.CSharp/src/DiagnosticDescriptors.cs new file mode 100644 index 00000000000..62ce716d8b8 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/DiagnosticDescriptors.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; + +namespace System.Windows.Forms.Analyzers +{ + internal static class DiagnosticDescriptors + { + private const string Category = "ApplicationConfiguration"; + + private static readonly LocalizableString s_localizableWFAC001Title + = new LocalizableResourceString(nameof(SR.WFAC001Title), SR.ResourceManager, typeof(SR)); + private static readonly LocalizableString s_localizableWFAC001Message + = new LocalizableResourceString(nameof(SR.WFAC001Message), SR.ResourceManager, typeof(SR)); + private static readonly LocalizableString s_localizableWFAC002Title + = new LocalizableResourceString(nameof(SR.WFAC002Title), SR.ResourceManager, typeof(SR)); + private static readonly LocalizableString s_localizableWFAC002Message + = new LocalizableResourceString(nameof(SR.WFAC002Message), SR.ResourceManager, typeof(SR)); + private static readonly LocalizableString s_localizableWFAC002MessageWithReason + = new LocalizableResourceString(nameof(SR.WFAC002MessageWithReason), SR.ResourceManager, typeof(SR)); + + public static readonly DiagnosticDescriptor s_errorUnsupportedProjectType + = new(id: "WFAC001", + title: s_localizableWFAC001Title, + messageFormat: s_localizableWFAC001Message, + category: Category, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor s_propertyCantBeSetToValue + = new(id: "WFAC002", + title: s_localizableWFAC002Title, + messageFormat: s_localizableWFAC002Message, + category: Category, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor s_propertyCantBeSetToValueWithReason + = new(id: "WFAC002", + title: s_localizableWFAC002Title, +#pragma warning disable RS1032 // Define diagnostic message correctly. Justification - exception messages end with a comma. + messageFormat: s_localizableWFAC002MessageWithReason, +#pragma warning restore RS1032 // Define diagnostic message correctly + category: Category, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/Properties/AssemblyInfo.cs b/src/System.Windows.Forms.Analyzers.CSharp/src/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..7724dd7c685 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("System.Windows.Forms.Analyzers.CSharp.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/Properties/launchSettings.json b/src/System.Windows.Forms.Analyzers.CSharp/src/Properties/launchSettings.json new file mode 100644 index 00000000000..854617a41cb --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "System.Windows.Forms.Analyzers.CSharp": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\..\\System.Windows.Forms\\tests\\IntegrationTests\\WinformsControlsTest\\WinformsControlsTest.csproj" + } + } +} \ No newline at end of file diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/Resources/SR.resx b/src/System.Windows.Forms.Analyzers.CSharp/src/Resources/SR.resx new file mode 100644 index 00000000000..ab1ebe5e0da --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/Resources/SR.resx @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Unsupported project type + + + Only projects with 'OutputType={0}' supported + + + Unsupported property value + + + ArgumentException: Project property '{0}' cannot be set to '{1}' + + + ArgumentException: Project property '{0}' cannot be set to '{1}'. Reason: {2} + + + Text "{0}" cannot be parsed. The expected text format is "{1}". + + + Value of '{0}' is not valid for font size unit. + + + Font '{0}' cannot be found. + + + Only TrueType fonts are supported. '{0}' is not a TrueType font. + + diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/System.Windows.Forms.Analyzers.CSharp.csproj b/src/System.Windows.Forms.Analyzers.CSharp/src/System.Windows.Forms.Analyzers.CSharp.csproj new file mode 100644 index 00000000000..504c682a10d --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/System.Windows.Forms.Analyzers.CSharp.csproj @@ -0,0 +1,41 @@ + + + + System.Windows.Forms.Analyzers + netstandard2.0 + Preview + enable + + true + + + true + WINFORMS_ANALYZERS + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + true + System + + + + diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ApplicationConfigurationGenerator.cs b/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ApplicationConfigurationGenerator.cs new file mode 100644 index 00000000000..387437cb0b2 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ApplicationConfigurationGenerator.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Linq; +using System.Windows.Forms.Analyzers; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace System.Windows.Forms.Generators +{ + [Generator] + internal class ApplicationConfigurationGenerator : ISourceGenerator + { + public void Execute(GeneratorExecutionContext context) + { + if (context.SyntaxReceiver is not ApplicationConfigurationSyntaxReceiver syntaxReceiver) + { + throw new InvalidOperationException("We were given the wrong syntax receiver."); + } + + if (syntaxReceiver.Nodes.Count == 0) + { + return; + } + + if (context.Compilation.Options.OutputKind != OutputKind.WindowsApplication && + // Starting in the 5.0.100 version of the .NET SDK, when OutputType is set to Exe, it is automatically changed to WinExe + // for WPF and Windows Forms apps that target any framework version, including .NET Framework. + // https://docs.microsoft.com/en-us/dotnet/core/compatibility/sdk/5.0/automatically-infer-winexe-output-type + context.Compilation.Options.OutputKind != OutputKind.ConsoleApplication) + { + context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.s_errorUnsupportedProjectType, Location.None, nameof(OutputKind.WindowsApplication))); + return; + } + + ApplicationConfig? projectConfig = ProjectFileReader.ReadApplicationConfig(context); + if (projectConfig is null) + { + return; + } + + string? code = ApplicationConfigurationInitializeBuilder.GenerateInitialize(projectNamespace: GetUserProjectNamespace(syntaxReceiver.Nodes[0]), projectConfig); + if (code is not null) + { + context.AddSource("ApplicationConfiguration.g.cs", code); + } + } + + private string? GetUserProjectNamespace(SyntaxNode node) + { + string? ns = null; + + if (node.Ancestors().FirstOrDefault(a => a is NamespaceDeclarationSyntax) is NamespaceDeclarationSyntax namespaceSyntax) + { + ns = namespaceSyntax.Name.ToString(); + } + + return ns; + } + + public void Initialize(GeneratorInitializationContext context) + { + context.RegisterForSyntaxNotifications(() => new ApplicationConfigurationSyntaxReceiver()); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilder.cs b/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilder.cs new file mode 100644 index 00000000000..05bb8e6f323 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilder.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text; +using System.Windows.Forms.Analyzers; + +namespace System.Windows.Forms.Generators +{ + internal static class ApplicationConfigurationInitializeBuilder + { + public static string GenerateInitialize(string? projectNamespace, ApplicationConfig projectConfig) + { + bool topLevelApp = string.IsNullOrWhiteSpace(projectNamespace); + string? defaultFont = projectConfig.DefaultFont?.ToString(); + string indent = topLevelApp ? string.Empty : " "; + return string.Format(topLevelApp ? TopLevelStatements : BoilerPlate, + topLevelApp ? string.Empty : projectNamespace, + GenerateCode(projectConfig, defaultFont, $"{indent} /// "), + GenerateCode(projectConfig, defaultFont, $"{indent} ")); + + static string GenerateCode(ApplicationConfig projectConfig, string? defaultFont, string indent) + { + StringBuilder code = new(); + if (projectConfig.EnableVisualStyles) + { + code.AppendLine($"{indent}Application.EnableVisualStyles();"); + } + + code.AppendLine($"{indent}Application.SetCompatibleTextRenderingDefault({projectConfig.UseCompatibleTextRendering.ToString().ToLowerInvariant()});"); + + if (!string.IsNullOrWhiteSpace(defaultFont)) + { + code.AppendLine($"{indent}Application.SetDefaultFont({defaultFont});"); + } + + // Don't append line as we don't need the trailing \r\n! + code.Append($"{indent}Application.SetHighDpiMode(HighDpiMode.{projectConfig.HighDpiMode});"); + + return code.ToString(); + } + } + + private const string BoilerPlate = @"// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Windows.Forms; + +namespace {0} +{{ + /// + /// Bootstrap the application configuration. + /// + [CompilerGenerated] + internal static partial class ApplicationConfiguration + {{ + /// + /// Bootstrap the application as follows: + /// +{1} + /// + /// + public static void Initialize() + {{ +{2} + }} + }} +}} +"; + + private const string TopLevelStatements = @"// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Windows.Forms; +{0} +/// +/// Bootstrap the application configuration. +/// +[CompilerGenerated] +internal static partial class ApplicationConfiguration +{{ + /// + /// Bootstrap the application as follows: + /// +{1} + /// + /// + public static void Initialize() + {{ + // Set STAThread + Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown); + Thread.CurrentThread.SetApartmentState(ApartmentState.STA); + +{2} + }} +}} +"; + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ApplicationConfigurationSyntaxReceiver.cs b/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ApplicationConfigurationSyntaxReceiver.cs new file mode 100644 index 00000000000..6e9b087e179 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ApplicationConfigurationSyntaxReceiver.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace System.Windows.Forms.Generators +{ + internal class ApplicationConfigurationSyntaxReceiver : ISyntaxReceiver + { + public List Nodes { get; } = new(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { +#pragma warning disable SA1513 // Closing brace should be followed by blank line + if (syntaxNode is InvocationExpressionSyntax + { + ArgumentList: + { + Arguments: { Count: < 1 } + }, + Expression: MemberAccessExpressionSyntax + { + Name: + { + Identifier: + { + ValueText: "Initialize" + } + }, + Expression: + MemberAccessExpressionSyntax // For: SourceGenerated.ApplicationConfiguration.Initialize() + { + Name: + { + Identifier: + { + ValueText: "ApplicationConfiguration" + } + } + } + or + IdentifierNameSyntax // For: ApplicationConfiguration.Initialize() with a using statement + { + Identifier: + { + ValueText: "ApplicationConfiguration" + } + } + } + }) + { + Nodes.Add(syntaxNode); + } +#pragma warning restore SA1513 // Closing brace should be followed by blank line + } + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ProjectFileReader.FontConverter.cs b/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ProjectFileReader.FontConverter.cs new file mode 100644 index 00000000000..36fcb932b2a --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ProjectFileReader.FontConverter.cs @@ -0,0 +1,176 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.Globalization; +using static System.Windows.Forms.Analyzers.ApplicationConfig; + +namespace System.Windows.Forms.Generators +{ + internal static partial class ProjectFileReader + { + // Copied from https://github.com/dotnet/runtime/blob/00ee1c18715723e62484c9bc8a14f517455fc3b3/src/libraries/System.Drawing.Common/src/System/Drawing/FontConverter.cs + internal static class FontConverter + { + private const string StylePrefix = "style="; + private static readonly CultureInfo s_culture = CultureInfo.InvariantCulture; + + public static FontDescriptor? ConvertFrom(string font) + { + font = font!.Trim(); + + // Expected string format: "name[, size[, units[, style=style1[, style2[...]]]]]" + // Example using 'vi-VN' culture: "Microsoft Sans Serif, 8,25pt, style=Italic, Bold" + if (font.Length == 0) + { + return null; + } + + char separator = s_culture.TextInfo.ListSeparator[0]; // For vi-VN: ',' + string fontName = font; // start with the assumption that only the font name was provided. + string? style = null; + string? sizeStr; + float fontSize = PropertyDefaultValue.FontSize; + FontStyle fontStyle = FontStyle.Regular; + GraphicsUnit units = GraphicsUnit.Point; + + // Get the index of the first separator (would indicate the end of the name in the string). + int nameIndex = font.IndexOf(separator); + + if (nameIndex < 0) + { + return new FontDescriptor(fontName, fontSize, fontStyle, units); + } + + // Some parameters are provided in addition to name. + fontName = font.Substring(0, nameIndex); + + if (nameIndex < font.Length - 1) + { + // Get the style index (if any). The size is a bit problematic because it can be formatted differently + // depending on the culture, we'll parse it last. + int styleIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(font, StylePrefix, CompareOptions.IgnoreCase); + + if (styleIndex != -1) + { + // style found. + style = font.Substring(styleIndex, font.Length - styleIndex); + + // Get the mid-substring containing the size information. + sizeStr = font.Substring(nameIndex + 1, styleIndex - nameIndex - 1); + } + else + { + // no style. + sizeStr = font.Substring(nameIndex + 1); + } + + // Parse size. + (string? size, string? unit) unitTokens = ParseSizeTokens(sizeStr, separator); + + if (unitTokens.size != null) + { + try + { + fontSize = (float)TypeDescriptor.GetConverter(typeof(float)).ConvertFromString(null, s_culture, unitTokens.size); + } + catch + { + // Exception from converter is too generic. + throw new ArgumentException(string.Format(SR.TextParseFailedFormat, + font, + $"name{separator} size[units[{separator} style=style1[{separator} style2{separator} ...]]]")); + } + } + + if (unitTokens.unit != null) + { + // ParseGraphicsUnits throws an ArgumentException if format is invalid. + units = ParseGraphicsUnits(unitTokens.unit); + } + + if (style != null) + { + // Parse FontStyle + style = style.Substring(6); // style string always starts with style= + string[] styleTokens = style.Split(separator); + + for (int tokenCount = 0; tokenCount < styleTokens.Length; tokenCount++) + { + string styleText = styleTokens[tokenCount]; + styleText = styleText.Trim(); + + fontStyle |= (FontStyle)Enum.Parse(typeof(FontStyle), styleText, true); + + // Enum.IsDefined doesn't do what we want on flags enums... + FontStyle validBits = FontStyle.Regular | FontStyle.Bold | FontStyle.Italic | FontStyle.Underline | FontStyle.Strikeout; + if ((fontStyle | validBits) != validBits) + { + throw new InvalidEnumArgumentException(nameof(style), (int)fontStyle, typeof(FontStyle)); + } + } + } + } + + return new FontDescriptor(fontName, fontSize, fontStyle, units); + } + + private static GraphicsUnit ParseGraphicsUnits(string units) => + units switch + { + // Display unit is not supported + // https://github.com/dotnet/runtime/blob/01b7e73cd378145264a7cb7a09365b41ed42b240/src/libraries/System.Drawing.Common/src/System/Drawing/FontConverter.cs#L446-L463 + // "display" => GraphicsUnit.Display, + "doc" => GraphicsUnit.Document, + "pt" => GraphicsUnit.Point, + "in" => GraphicsUnit.Inch, + "mm" => GraphicsUnit.Millimeter, + "px" => GraphicsUnit.Pixel, + "world" => GraphicsUnit.World, + _ => throw new ArgumentException(string.Format(SR.InvalidArgumentValueFontConverter, units), nameof(units)), + }; + + private static (string?, string?) ParseSizeTokens(string text, char separator) + { + string? size = null; + string? units = null; + + text = text.Trim(); + + int length = text.Length; + int splitPoint; + + if (length > 0) + { + // text is expected to have a format like " 8,25pt, ". Leading and trailing spaces (trimmed above), + // last comma, unit and decimal value may not appear. We need to make it ####.##CC + for (splitPoint = 0; splitPoint < length; splitPoint++) + { + if (char.IsLetter(text[splitPoint])) + { + break; + } + } + + char[] trimChars = new char[] { separator, ' ' }; + + if (splitPoint > 0) + { + size = text.Substring(0, splitPoint); + // Trimming spaces between size and units. + size = size.Trim(trimChars); + } + + if (splitPoint < length) + { + units = text.Substring(splitPoint); + units = units.TrimEnd(trimChars); + } + } + + return (size, units); + } + } + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ProjectFileReader.cs b/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ProjectFileReader.cs new file mode 100644 index 00000000000..d4e56add275 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/src/System/Windows/Forms/Generators/ProjectFileReader.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Windows.Forms.Analyzers; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using static System.Windows.Forms.Analyzers.ApplicationConfig; + +namespace System.Windows.Forms.Generators +{ + internal static partial class ProjectFileReader + { + public static ApplicationConfig? ReadApplicationConfig(GeneratorExecutionContext context) + { + Diagnostic? diagnostic; + if (!TryReadBool(context.AnalyzerConfigOptions, PropertyNameCSharp.EnableVisualStyles, + defaultValue: PropertyDefaultValue.EnableVisualStyles, + out bool enableVisualStyles, out diagnostic) || + !TryReadBool(context.AnalyzerConfigOptions, PropertyNameCSharp.UseCompatibleTextRendering, + defaultValue: PropertyDefaultValue.UseCompatibleTextRendering, + out bool useCompatibleTextRendering, out diagnostic) || + !TryReadFont(context.AnalyzerConfigOptions, out FontDescriptor? font, out diagnostic) || + !TryReadHighDpiMode(context.AnalyzerConfigOptions, out HighDpiMode highDpiMode, out diagnostic)) + { + context.ReportDiagnostic(diagnostic!); + return null; + } + + ApplicationConfig projectConfig = new() + { + EnableVisualStyles = enableVisualStyles, + DefaultFont = font, + HighDpiMode = highDpiMode, + UseCompatibleTextRendering = useCompatibleTextRendering + }; + + return projectConfig; + } + + private static bool TryReadBool(AnalyzerConfigOptionsProvider configOptions, string propertyName, bool defaultValue, out bool value, out Diagnostic? diagnostic) + { + value = defaultValue; + diagnostic = null; + + if (!configOptions.GetMSBuildProperty(propertyName, out string? rawValue) || + rawValue == string.Empty) + { + // The property is either not defined explicitly, or the value is "". All good, use the default. + return true; + } + + if (!bool.TryParse(rawValue, out value)) + { + diagnostic = Diagnostic.Create(DiagnosticDescriptors.s_propertyCantBeSetToValue, + Location.None, + propertyName, + rawValue); + value = defaultValue; + return false; + } + + return true; + } + + private static bool TryReadFont(AnalyzerConfigOptionsProvider configOptions, out FontDescriptor? font, out Diagnostic? diagnostic) + { + font = null; + diagnostic = null; + + if (!configOptions.GetMSBuildProperty(PropertyNameCSharp.DefaultFont, out string? rawValue) || + rawValue == string.Empty) + { + // The property is either not defined explicitly, or the value is "". All good, use the default. + return true; + } + + try + { + // In .NET runtime the font is validated via GDI+ to see whether it can be mapped to a valid font family. + // We don't have access to Font (though we can with some gymnastics) or FontConverter (at all), so our + // font validation logic is not as exhaustive as it is in .NET runtime. + // With that it is possible that the value is not a valid font (e.g. 'Style=Bold' or '11px'), and which + // will lead to runtime failures when we execute SetDefaultFont(new FontFamily('Style=Bold')). + font = FontConverter.ConvertFrom(rawValue!); + return true; + } + catch (Exception ex) + { + diagnostic = Diagnostic.Create(DiagnosticDescriptors.s_propertyCantBeSetToValueWithReason, + Location.None, + PropertyNameCSharp.DefaultFont, + rawValue, + ex.Message); + } + + return false; + } + + private static bool TryReadHighDpiMode(AnalyzerConfigOptionsProvider configOptions, out HighDpiMode highDpiMode, out Diagnostic? diagnostic) + { + highDpiMode = PropertyDefaultValue.DpiMode; + diagnostic = null; + + if (!configOptions.GetMSBuildProperty(PropertyNameCSharp.HighDpiMode, out string? rawValue) || + rawValue == string.Empty) + { + // The property is either not defined explicitly, or the value is "". All good, use the default. + return true; + } + + if (!Enum.TryParse(rawValue, true, out highDpiMode) || + !Enum.IsDefined(typeof(HighDpiMode), highDpiMode)) + { + diagnostic = Diagnostic.Create(DiagnosticDescriptors.s_propertyCantBeSetToValue, + Location.None, + PropertyNameCSharp.HighDpiMode, + rawValue); + highDpiMode = PropertyDefaultValue.DpiMode; + return false; + } + + return true; + } + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/CompilerAnalyzerConfigOptions.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/CompilerAnalyzerConfigOptions.cs new file mode 100644 index 00000000000..a0bbb25b0b7 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/CompilerAnalyzerConfigOptions.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace System.Windows.Forms.Analyzers.Tests +{ + // Borrowed from https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerConfigOptions.cs + + [ExcludeFromCodeCoverage] + internal sealed class CompilerAnalyzerConfigOptions : AnalyzerConfigOptions + { + public static CompilerAnalyzerConfigOptions Empty { get; } = new CompilerAnalyzerConfigOptions(ImmutableDictionary.Create()); + + private readonly ImmutableDictionary _backing; + + public CompilerAnalyzerConfigOptions(ImmutableDictionary properties) + { + _backing = properties; + } + + public override bool TryGetValue(string key, [NotNullWhen(true)] out string? value) => _backing.TryGetValue(key, out value); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/CompilerAnalyzerConfigOptionsProvider.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/CompilerAnalyzerConfigOptionsProvider.cs new file mode 100644 index 00000000000..3d28cf5af7f --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/CompilerAnalyzerConfigOptionsProvider.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace System.Windows.Forms.Analyzers.Tests +{ + // Borrowed from https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilerAnalyzerConfigOptionsProvider.cs + + [ExcludeFromCodeCoverage] + internal sealed class CompilerAnalyzerConfigOptionsProvider : AnalyzerConfigOptionsProvider + { + private readonly ImmutableDictionary _treeDict; + + public static CompilerAnalyzerConfigOptionsProvider Empty { get; } + = new CompilerAnalyzerConfigOptionsProvider( + ImmutableDictionary.Empty, + CompilerAnalyzerConfigOptions.Empty); + + internal CompilerAnalyzerConfigOptionsProvider( + ImmutableDictionary treeDict, + AnalyzerConfigOptions globalOptions) + { + _treeDict = treeDict; + GlobalOptions = globalOptions; + } + + public override AnalyzerConfigOptions GlobalOptions { get; } + + public override AnalyzerConfigOptions GetOptions(SyntaxTree tree) + => _treeDict.TryGetValue(tree, out var options) ? options : CompilerAnalyzerConfigOptions.Empty; + + public override AnalyzerConfigOptions GetOptions(AdditionalText textFile) + => _treeDict.TryGetValue(textFile, out var options) ? options : CompilerAnalyzerConfigOptions.Empty; + + internal CompilerAnalyzerConfigOptionsProvider WithAdditionalTreeOptions(ImmutableDictionary treeDict) + => new(_treeDict.AddRange(treeDict), GlobalOptions); + + internal CompilerAnalyzerConfigOptionsProvider WithGlobalOptions(AnalyzerConfigOptions globalOptions) + => new(_treeDict, globalOptions); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System.Windows.Forms.Analyzers.CSharp.Tests.csproj b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System.Windows.Forms.Analyzers.CSharp.Tests.csproj new file mode 100644 index 00000000000..e5f38e3f97a --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System.Windows.Forms.Analyzers.CSharp.Tests.csproj @@ -0,0 +1,33 @@ + + + + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationGeneratorTests.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationGeneratorTests.cs new file mode 100644 index 00000000000..dfcc0cbd1f8 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationGeneratorTests.cs @@ -0,0 +1,207 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text; +using System.Windows.Forms.Analyzers; +using System.Windows.Forms.Analyzers.Tests; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; +using Xunit; +using static System.Windows.Forms.Analyzers.ApplicationConfig; + +namespace System.Windows.Forms.Generators.Tests +{ + public partial class ApplicationConfigurationGeneratorTests + { + private const string SourceCompilable = @" +namespace MyProject +{ + class Program + { + static void Main() + { + ApplicationConfiguration.Initialize(); + } + } +}"; + private const string SourceCompilationFailed = @" +namespace MyProject +{ + class Program + { + static void Main() + { + {|CS0103:ApplicationConfiguration|}.Initialize(); + } + } +}"; + + public static IEnumerable UnsupportedProjectTypes_TestData() + { + foreach (OutputKind projectType in Enum.GetValues(typeof(OutputKind))) + { + if (projectType != OutputKind.ConsoleApplication && + projectType != OutputKind.WindowsApplication) + { + yield return new object[] { projectType }; + } + } + } + + [Theory] + [MemberData(nameof(UnsupportedProjectTypes_TestData))] + public async Task ApplicationConfigurationGenerator_GenerateInitialize_fails_if_project_type_unsupported(OutputKind projectType) + { + var test = new CSharpSourceGeneratorTest + { + TestState = + { + OutputKind = projectType, + Sources = { SourceCompilationFailed }, + ExpectedDiagnostics = + { + DiagnosticResult.CompilerError("WFAC001").WithArguments("WindowsApplication"), + } + }, + }; + + await test.RunAsync().ConfigureAwait(false); + } + + [Theory] + [InlineData(OutputKind.ConsoleApplication)] + [InlineData(OutputKind.WindowsApplication)] + public async Task ApplicationConfigurationGenerator_GenerateInitialize_pass_if_supported_project_type(OutputKind projectType) + { + SourceText generatedCode = LoadFileContent("GenerateInitialize_default_boilerplate"); + + var test = new CSharpSourceGeneratorTest + { + TestState = + { + OutputKind = projectType, + Sources = { SourceCompilable }, + GeneratedSources = + { + (typeof(ApplicationConfigurationGenerator), "ApplicationConfiguration.g.cs", generatedCode), + }, + }, + }; + + await test.RunAsync().ConfigureAwait(false); + } + + [Fact] + public async Task ApplicationConfigurationGenerator_GenerateInitialize_default_boilerplate() + { + SourceText generatedCode = LoadFileContent("GenerateInitialize_default_boilerplate"); + + var test = new CSharpSourceGeneratorTest + { + TestState = + { + OutputKind = OutputKind.WindowsApplication, + Sources = { SourceCompilable }, + GeneratedSources = + { + (typeof(ApplicationConfigurationGenerator), "ApplicationConfiguration.g.cs", generatedCode), + }, + }, + }; + + await test.RunAsync().ConfigureAwait(false); + } + + [Fact] + public async Task ApplicationConfigurationGenerator_GenerateInitialize_user_settings_boilerplate() + { + SourceText generatedCode = LoadFileContent("GenerateInitialize_user_settings_boilerplate"); + + var test = new CSharpSourceGeneratorTest + { + GlobalOptions = + { + ($"build_property.{PropertyNameCSharp.DefaultFont}", "Microsoft Sans Serif, 8.25px"), + ($"build_property.{PropertyNameCSharp.EnableVisualStyles}", ""), + ($"build_property.{PropertyNameCSharp.HighDpiMode}", HighDpiMode.SystemAware.ToString()), + ($"build_property.{PropertyNameCSharp.UseCompatibleTextRendering}", "true"), + }, + TestState = + { + OutputKind = OutputKind.WindowsApplication, + Sources = { SourceCompilable }, + GeneratedSources = + { + (typeof(ApplicationConfigurationGenerator), "ApplicationConfiguration.g.cs", generatedCode), + }, + }, + }; + + await test.RunAsync().ConfigureAwait(false); + } + + [Fact] + public async Task ApplicationConfigurationGenerator_GenerateInitialize_default_top_level() + { + const string source = @" +ApplicationConfiguration.Initialize(); +"; + + SourceText generatedCode = LoadFileContent("GenerateInitialize_default_top_level"); + + var test = new CSharpSourceGeneratorTest + { + TestState = + { + OutputKind = OutputKind.WindowsApplication, + Sources = { source }, + GeneratedSources = + { + (typeof(ApplicationConfigurationGenerator), "ApplicationConfiguration.g.cs", generatedCode), + }, + }, + }; + + await test.RunAsync().ConfigureAwait(false); + } + + [Fact] + public async Task ApplicationConfigurationGenerator_GenerateInitialize_user_settings_top_level() + { + const string source = @" +ApplicationConfiguration.Initialize(); +"; + + SourceText generatedCode = LoadFileContent("GenerateInitialize_user_top_level"); + + var test = new CSharpSourceGeneratorTest + { + GlobalOptions = + { + ($"build_property.{PropertyNameCSharp.DefaultFont}", "Microsoft Sans Serif, 8.25px"), + ($"build_property.{PropertyNameCSharp.EnableVisualStyles}", ""), + ($"build_property.{PropertyNameCSharp.HighDpiMode}", HighDpiMode.SystemAware.ToString()), + ($"build_property.{PropertyNameCSharp.UseCompatibleTextRendering}", "true"), + }, + TestState = + { + OutputKind = OutputKind.WindowsApplication, + Sources = { source }, + GeneratedSources = + { + (typeof(ApplicationConfigurationGenerator), "ApplicationConfiguration.g.cs", generatedCode), + }, + }, + }; + + await test.RunAsync().ConfigureAwait(false); + } + + private SourceText LoadFileContent(string testName) + => SourceText.From( + File.ReadAllText($@"System\Windows\Forms\Generators\MockData\{GetType().Name}.{testName}.cs"), + Encoding.UTF8); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_DefaultFont=Tahoma.verified.txt b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_DefaultFont=Tahoma.verified.txt new file mode 100644 index 00000000000..73352e47ad1 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_DefaultFont=Tahoma.verified.txt @@ -0,0 +1,34 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Windows.Forms; + +/// +/// Bootstrap the application configuration. +/// +[CompilerGenerated] +internal static partial class ApplicationConfiguration +{ + /// + /// Bootstrap the application as follows: + /// + /// Application.EnableVisualStyles(); + /// Application.SetCompatibleTextRenderingDefault(true); + /// Application.SetDefaultFont(new Font(new FontFamily("Tahoma"), 12f, (FontStyle)0, (GraphicsUnit)3)); + /// Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + /// + /// + public static void Initialize() + { + // Set STAThread + Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown); + Thread.CurrentThread.SetApartmentState(ApartmentState.STA); + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(true); + Application.SetDefaultFont(new Font(new FontFamily("Tahoma"), 12f, (FontStyle)0, (GraphicsUnit)3)); + Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_DefaultFont=default.verified.txt b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_DefaultFont=default.verified.txt new file mode 100644 index 00000000000..336c79ed031 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_DefaultFont=default.verified.txt @@ -0,0 +1,34 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Windows.Forms; + +/// +/// Bootstrap the application configuration. +/// +[CompilerGenerated] +internal static partial class ApplicationConfiguration +{ + /// + /// Bootstrap the application as follows: + /// + /// Application.EnableVisualStyles(); + /// Application.SetCompatibleTextRenderingDefault(true); + /// Application.SetDefaultFont(new Font(Control.DefaultFont.FontFamily, 12f, (FontStyle)3, (GraphicsUnit)6)); + /// Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + /// + /// + public static void Initialize() + { + // Set STAThread + Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown); + Thread.CurrentThread.SetApartmentState(ApartmentState.STA); + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(true); + Application.SetDefaultFont(new Font(Control.DefaultFont.FontFamily, 12f, (FontStyle)3, (GraphicsUnit)6)); + Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_DefaultFont=null.verified.txt b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_DefaultFont=null.verified.txt new file mode 100644 index 00000000000..fe67c265d4d --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_DefaultFont=null.verified.txt @@ -0,0 +1,32 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Windows.Forms; + +/// +/// Bootstrap the application configuration. +/// +[CompilerGenerated] +internal static partial class ApplicationConfiguration +{ + /// + /// Bootstrap the application as follows: + /// + /// Application.EnableVisualStyles(); + /// Application.SetCompatibleTextRenderingDefault(false); + /// Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + /// + /// + public static void Initialize() + { + // Set STAThread + Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown); + Thread.CurrentThread.SetApartmentState(ApartmentState.STA); + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_EnableVisualStyles=false.verified.txt b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_EnableVisualStyles=false.verified.txt new file mode 100644 index 00000000000..2b72d99d38e --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_EnableVisualStyles=false.verified.txt @@ -0,0 +1,30 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Windows.Forms; + +/// +/// Bootstrap the application configuration. +/// +[CompilerGenerated] +internal static partial class ApplicationConfiguration +{ + /// + /// Bootstrap the application as follows: + /// + /// Application.SetCompatibleTextRenderingDefault(false); + /// Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + /// + /// + public static void Initialize() + { + // Set STAThread + Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown); + Thread.CurrentThread.SetApartmentState(ApartmentState.STA); + + Application.SetCompatibleTextRenderingDefault(false); + Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_EnableVisualStyles=true.verified.txt b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_EnableVisualStyles=true.verified.txt new file mode 100644 index 00000000000..fe67c265d4d --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_EnableVisualStyles=true.verified.txt @@ -0,0 +1,32 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Windows.Forms; + +/// +/// Bootstrap the application configuration. +/// +[CompilerGenerated] +internal static partial class ApplicationConfiguration +{ + /// + /// Bootstrap the application as follows: + /// + /// Application.EnableVisualStyles(); + /// Application.SetCompatibleTextRenderingDefault(false); + /// Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + /// + /// + public static void Initialize() + { + // Set STAThread + Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown); + Thread.CurrentThread.SetApartmentState(ApartmentState.STA); + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_UseCompTextRendering=false.verified.txt b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_UseCompTextRendering=false.verified.txt new file mode 100644 index 00000000000..fe67c265d4d --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_UseCompTextRendering=false.verified.txt @@ -0,0 +1,32 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Windows.Forms; + +/// +/// Bootstrap the application configuration. +/// +[CompilerGenerated] +internal static partial class ApplicationConfiguration +{ + /// + /// Bootstrap the application as follows: + /// + /// Application.EnableVisualStyles(); + /// Application.SetCompatibleTextRenderingDefault(false); + /// Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + /// + /// + public static void Initialize() + { + // Set STAThread + Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown); + Thread.CurrentThread.SetApartmentState(ApartmentState.STA); + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_UseCompTextRendering=true.verified.txt b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_UseCompTextRendering=true.verified.txt new file mode 100644 index 00000000000..5fcfb37097c --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.GenerateInitialize_UseCompTextRendering=true.verified.txt @@ -0,0 +1,32 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Windows.Forms; + +/// +/// Bootstrap the application configuration. +/// +[CompilerGenerated] +internal static partial class ApplicationConfiguration +{ + /// + /// Bootstrap the application as follows: + /// + /// Application.EnableVisualStyles(); + /// Application.SetCompatibleTextRenderingDefault(true); + /// Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + /// + /// + public static void Initialize() + { + // Set STAThread + Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown); + Thread.CurrentThread.SetApartmentState(ApartmentState.STA); + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(true); + Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.cs new file mode 100644 index 00000000000..2250c3e1dc6 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ApplicationConfigurationInitializeBuilderTests.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Windows.Forms.Analyzers; +using VerifyXunit; +using Xunit; +using static System.Windows.Forms.Analyzers.ApplicationConfig; + +namespace System.Windows.Forms.Generators.Tests +{ + [UsesVerify] + public partial class ApplicationConfigurationInitializeBuilderTests + { + [Theory] + [InlineData(null, "default_top_level")] + [InlineData("", "default_top_level")] + [InlineData(" ", "default_top_level")] + [InlineData("\t", "default_top_level")] + [InlineData("MyProject", "default_boilerplate")] + public void ApplicationConfigurationInitializeBuilder_GenerateInitialize_can_handle_namespace(string ns, string expectedFileName) + { + string expected = File.ReadAllText($@"System\Windows\Forms\Generators\MockData\{GetType().Name}.{expectedFileName}.cs"); + + string output = ApplicationConfigurationInitializeBuilder.GenerateInitialize(ns, + new ApplicationConfig + { + DefaultFont = null, + EnableVisualStyles = PropertyDefaultValue.EnableVisualStyles, + HighDpiMode = PropertyDefaultValue.DpiMode, + UseCompatibleTextRendering = PropertyDefaultValue.UseCompatibleTextRendering + }); + + Assert.Equal(expected, output); + } + + internal static TheoryData<(object, string)> GenerateInitializeData() + => new() + { + // EnableVisualStyles: false, true + (new ApplicationConfig + { + DefaultFont = null, + EnableVisualStyles = false, + HighDpiMode = PropertyDefaultValue.DpiMode, + UseCompatibleTextRendering = PropertyDefaultValue.UseCompatibleTextRendering + }, "EnableVisualStyles=false"), + (new ApplicationConfig + { + DefaultFont = null, + EnableVisualStyles = true, + HighDpiMode = PropertyDefaultValue.DpiMode, + UseCompatibleTextRendering = PropertyDefaultValue.UseCompatibleTextRendering + }, "EnableVisualStyles=true"), + + // UseCompatibleTextRendering: false, true + (new ApplicationConfig + { + DefaultFont = null, + EnableVisualStyles = PropertyDefaultValue.EnableVisualStyles, + HighDpiMode = PropertyDefaultValue.DpiMode, + UseCompatibleTextRendering = false + }, "UseCompTextRendering=false"), + (new ApplicationConfig + { + DefaultFont = null, + EnableVisualStyles = PropertyDefaultValue.EnableVisualStyles, + HighDpiMode = PropertyDefaultValue.DpiMode, + UseCompatibleTextRendering = true + }, "UseCompTextRendering=true"), + + // DefaultFont: null, FontDescriptor + (new ApplicationConfig + { + DefaultFont = null, + EnableVisualStyles = PropertyDefaultValue.EnableVisualStyles, + HighDpiMode = PropertyDefaultValue.DpiMode, + UseCompatibleTextRendering = false + }, "DefaultFont=null"), + (new ApplicationConfig + { + DefaultFont = new FontDescriptor(string.Empty, 12, FontStyle.Bold | FontStyle.Italic, GraphicsUnit.Millimeter), + EnableVisualStyles = PropertyDefaultValue.EnableVisualStyles, + HighDpiMode = PropertyDefaultValue.DpiMode, + UseCompatibleTextRendering = true + }, "DefaultFont=default"), + (new ApplicationConfig + { + DefaultFont = new FontDescriptor("Tahoma", 12, FontStyle.Regular, GraphicsUnit.Point), + EnableVisualStyles = PropertyDefaultValue.EnableVisualStyles, + HighDpiMode = PropertyDefaultValue.DpiMode, + UseCompatibleTextRendering = true + }, "DefaultFont=Tahoma"), + }; + + [Theory] + [MemberData(nameof(GenerateInitializeData))] + public Task ApplicationConfigurationInitializeBuilder_GenerateInitialize((/* ApplicationConfig */object config, string testName) data) + { + string output = ApplicationConfigurationInitializeBuilder.GenerateInitialize(null, (ApplicationConfig)data.config); + + return Verifier.Verify(output) + .UseMethodName("GenerateInitialize") + .UseTextForParameters(data.testName); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationGeneratorTests.GenerateInitialize_default_boilerplate.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationGeneratorTests.GenerateInitialize_default_boilerplate.cs new file mode 100644 index 00000000000..75d65e5d049 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationGeneratorTests.GenerateInitialize_default_boilerplate.cs @@ -0,0 +1,30 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Windows.Forms; + +namespace MyProject +{ + /// + /// Bootstrap the application configuration. + /// + [CompilerGenerated] + internal static partial class ApplicationConfiguration + { + /// + /// Bootstrap the application as follows: + /// + /// Application.EnableVisualStyles(); + /// Application.SetCompatibleTextRenderingDefault(false); + /// Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + /// + /// + public static void Initialize() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.{|CS0117:SetHighDpiMode|}({|CS0103:HighDpiMode|}.PerMonitorV2); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationGeneratorTests.GenerateInitialize_default_top_level.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationGeneratorTests.GenerateInitialize_default_top_level.cs new file mode 100644 index 00000000000..6d65d4b4262 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationGeneratorTests.GenerateInitialize_default_top_level.cs @@ -0,0 +1,32 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Windows.Forms; + +/// +/// Bootstrap the application configuration. +/// +[CompilerGenerated] +internal static partial class ApplicationConfiguration +{ + /// + /// Bootstrap the application as follows: + /// + /// Application.EnableVisualStyles(); + /// Application.SetCompatibleTextRenderingDefault(false); + /// Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + /// + /// + public static void Initialize() + { + // Set STAThread + Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown); + Thread.CurrentThread.SetApartmentState(ApartmentState.STA); + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.{|CS0117:SetHighDpiMode|}({|CS0103:HighDpiMode|}.PerMonitorV2); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationGeneratorTests.GenerateInitialize_user_settings_boilerplate.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationGeneratorTests.GenerateInitialize_user_settings_boilerplate.cs new file mode 100644 index 00000000000..f2a35cc7e9a --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationGeneratorTests.GenerateInitialize_user_settings_boilerplate.cs @@ -0,0 +1,32 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Windows.Forms; + +namespace MyProject +{ + /// + /// Bootstrap the application configuration. + /// + [CompilerGenerated] + internal static partial class ApplicationConfiguration + { + /// + /// Bootstrap the application as follows: + /// + /// Application.EnableVisualStyles(); + /// Application.SetCompatibleTextRenderingDefault(true); + /// Application.SetDefaultFont(new Font(new FontFamily("Microsoft Sans Serif"), 8.25f, (FontStyle)0, (GraphicsUnit)2)); + /// Application.SetHighDpiMode(HighDpiMode.SystemAware); + /// + /// + public static void Initialize() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(true); + Application.{|CS0117:SetDefaultFont|}(new Font(new FontFamily("Microsoft Sans Serif"), 8.25f, (FontStyle)0, (GraphicsUnit)2)); + Application.{|CS0117:SetHighDpiMode|}({|CS0103:HighDpiMode|}.SystemAware); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationGeneratorTests.GenerateInitialize_user_top_level.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationGeneratorTests.GenerateInitialize_user_top_level.cs new file mode 100644 index 00000000000..049d97a3bd6 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationGeneratorTests.GenerateInitialize_user_top_level.cs @@ -0,0 +1,34 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Windows.Forms; + +/// +/// Bootstrap the application configuration. +/// +[CompilerGenerated] +internal static partial class ApplicationConfiguration +{ + /// + /// Bootstrap the application as follows: + /// + /// Application.EnableVisualStyles(); + /// Application.SetCompatibleTextRenderingDefault(true); + /// Application.SetDefaultFont(new Font(new FontFamily("Microsoft Sans Serif"), 8.25f, (FontStyle)0, (GraphicsUnit)2)); + /// Application.SetHighDpiMode(HighDpiMode.SystemAware); + /// + /// + public static void Initialize() + { + // Set STAThread + Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown); + Thread.CurrentThread.SetApartmentState(ApartmentState.STA); + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(true); + Application.{|CS0117:SetDefaultFont|}(new Font(new FontFamily("Microsoft Sans Serif"), 8.25f, (FontStyle)0, (GraphicsUnit)2)); + Application.{|CS0117:SetHighDpiMode|}({|CS0103:HighDpiMode|}.SystemAware); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationInitializeBuilderTests.default_boilerplate.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationInitializeBuilderTests.default_boilerplate.cs new file mode 100644 index 00000000000..adc69c6eae3 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationInitializeBuilderTests.default_boilerplate.cs @@ -0,0 +1,30 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Windows.Forms; + +namespace MyProject +{ + /// + /// Bootstrap the application configuration. + /// + [CompilerGenerated] + internal static partial class ApplicationConfiguration + { + /// + /// Bootstrap the application as follows: + /// + /// Application.EnableVisualStyles(); + /// Application.SetCompatibleTextRenderingDefault(false); + /// Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + /// + /// + public static void Initialize() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationInitializeBuilderTests.default_top_level.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationInitializeBuilderTests.default_top_level.cs new file mode 100644 index 00000000000..fe67c265d4d --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/MockData/ApplicationConfigurationInitializeBuilderTests.default_top_level.cs @@ -0,0 +1,32 @@ +// + +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Windows.Forms; + +/// +/// Bootstrap the application configuration. +/// +[CompilerGenerated] +internal static partial class ApplicationConfiguration +{ + /// + /// Bootstrap the application as follows: + /// + /// Application.EnableVisualStyles(); + /// Application.SetCompatibleTextRenderingDefault(false); + /// Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + /// + /// + public static void Initialize() + { + // Set STAThread + Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown); + Thread.CurrentThread.SetApartmentState(ApartmentState.STA); + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ProjectFileReaderTests.FontConverter.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ProjectFileReaderTests.FontConverter.cs new file mode 100644 index 00000000000..8c0d7199b1f --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ProjectFileReaderTests.FontConverter.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.Globalization; +using Xunit; +using static System.Windows.Forms.Analyzers.ApplicationConfig; +using static System.Windows.Forms.Generators.ProjectFileReader; + +namespace System.Windows.Forms.Generators.Tests +{ + public partial class ProjectFileReaderTests + { + public class FontConverterTest + { + private static readonly char s_separator = CultureInfo.CurrentCulture.TextInfo.ListSeparator[0]; + + public static TheoryData TestConvertFormData() + => new() + { + { $"Courier New", "Courier New", PropertyDefaultValue.FontSize, (int)GraphicsUnit.Point, (int)FontStyle.Regular }, + { $"Courier New{s_separator} 11", "Courier New", 11f, (int)GraphicsUnit.Point, (int)FontStyle.Regular }, + { $"Arial{s_separator} 11px", "Arial", 11f, (int)GraphicsUnit.Pixel, (int)FontStyle.Regular }, + { $"Courier New{s_separator} 11 px", "Courier New", 11f, (int)GraphicsUnit.Pixel, (int)FontStyle.Regular }, + { $"Courier New{s_separator} 11 px{s_separator} style=Regular", "Courier New", 11f, (int)GraphicsUnit.Pixel, (int)FontStyle.Regular }, + { $"Courier New{s_separator} style=Bold", "Courier New", PropertyDefaultValue.FontSize, (int)GraphicsUnit.Point, (int)FontStyle.Bold }, + { $"Courier New{s_separator} 11 px{s_separator} style=Bold{s_separator} Italic", "Courier New", 11f, (int)GraphicsUnit.Pixel, (int)(FontStyle.Bold | FontStyle.Italic) }, + { $"Courier New{s_separator} 11 px{s_separator} style=Regular, Italic", "Courier New", 11f, (int)GraphicsUnit.Pixel, (int)(FontStyle.Regular | FontStyle.Italic) }, + { $"Courier New{s_separator} 11 px{s_separator} style=Bold{s_separator} Italic{s_separator} Strikeout", "Courier New", 11f, (int)GraphicsUnit.Pixel, (int)(FontStyle.Bold | FontStyle.Italic | FontStyle.Strikeout) }, + { $"Arial{s_separator} 11 px{s_separator} style=Bold, Italic, Strikeout", "Arial", 11f, (int)GraphicsUnit.Pixel, (int)(FontStyle.Bold | FontStyle.Italic | FontStyle.Strikeout) }, + { $"arIAL{s_separator} 10{s_separator} style=bold", "arIAL", 10f, (int)GraphicsUnit.Point, (int)FontStyle.Bold }, + { $"Arial{s_separator} 10{s_separator}", "Arial", 10f, (int)GraphicsUnit.Point, (int)FontStyle.Regular }, + { $"Arial{s_separator}", "Arial", PropertyDefaultValue.FontSize, (int)GraphicsUnit.Point, (int)FontStyle.Regular }, + { $"Arial{s_separator} 10{s_separator} style=12", "Arial", 10f, (int)GraphicsUnit.Point, (int)(FontStyle.Underline | FontStyle.Strikeout) }, + { $"Courier New{s_separator} Style=Bold", "Courier New", PropertyDefaultValue.FontSize, (int)GraphicsUnit.Point, (int)FontStyle.Bold }, // FullFramework style keyword is case sensitive. + { $"{s_separator} 10{s_separator} style=bold", "", 10f, (int)GraphicsUnit.Point, (int)FontStyle.Bold }, + + // NOTE: in .NET runtime these tests will result in FontName='', but the implementation relies on GDI+, which we don't have... + { $"11px{s_separator} Style=Bold", $"11px", PropertyDefaultValue.FontSize, (int)GraphicsUnit.Point, (int)FontStyle.Bold }, + { $"11px", "11px", PropertyDefaultValue.FontSize, (int)GraphicsUnit.Point, (int)FontStyle.Regular }, + { $"Style=Bold", "Style=Bold", PropertyDefaultValue.FontSize, (int)GraphicsUnit.Point, (int)FontStyle.Regular }, + }; + + [Theory] + [MemberData(nameof(TestConvertFormData))] + internal void TestConvertFrom(string input, string expectedName, float expectedSize, GraphicsUnit expectedUnits, FontStyle expectedFontStyle) + { + FontDescriptor font = FontConverter.ConvertFrom(input)!; + + Assert.Equal(expectedName, font.Name); + Assert.Equal(expectedSize, font.Size); + Assert.Equal(expectedUnits, font.Unit); + Assert.Equal(expectedFontStyle, font.Style); + } + + public static TheoryData ArgumentExceptionFontConverterData() + => new() + { + { $"Courier New{s_separator} 11 px{s_separator} type=Bold{s_separator} Italic" }, + { $"Courier New{s_separator} {s_separator} Style=Bold" }, + { $"Courier New{s_separator} 11{s_separator} Style=" }, + { $"Courier New{s_separator} 11{s_separator} Style=RandomEnum" }, + { $"Arial{s_separator} 10{s_separator} style=bold{s_separator}" }, + { $"Arial{s_separator} 10{s_separator} style=null" }, + { $"Arial{s_separator} 10{s_separator} style=abc#" }, + { $"Arial{s_separator} 10{s_separator} style=##" }, + { $"Arial{s_separator} 10display{s_separator} style=bold" }, + { $"Arial{s_separator} 10style{s_separator} style=bold" }, + }; + + [Theory] + [MemberData(nameof(ArgumentExceptionFontConverterData))] + public void InvalidInputThrowsArgumentException(string input) + { + Assert.Throws(() => FontConverter.ConvertFrom(input)); + } + + public static TheoryData InvalidEnumArgumentExceptionFontConverterData() + => new() + { + { $"Arial{s_separator} 10{s_separator} style=56", "style" }, + { $"Arial{s_separator} 10{s_separator} style=-1", "style" }, + }; + + [Theory] + [MemberData(nameof(InvalidEnumArgumentExceptionFontConverterData))] + public void InvalidInputThrowsInvalidEnumArgumentException(string input, string paramName) + { + Assert.Throws(paramName, () => FontConverter.ConvertFrom(input)); + } + + [Fact] + public void EmptyStringInput() + { + FontDescriptor? font = FontConverter.ConvertFrom(string.Empty); + Assert.Null(font); + } + } + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ProjectFileReaderTests.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ProjectFileReaderTests.cs new file mode 100644 index 00000000000..0a4223a51ff --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/System/Windows/Forms/Generators/ProjectFileReaderTests.cs @@ -0,0 +1,208 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Globalization; +using System.Windows.Forms.Analyzers; +using System.Windows.Forms.Analyzers.Tests; +using System.Windows.Forms.TestUtilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Xunit; +using Xunit.Abstractions; +using static System.Windows.Forms.Analyzers.ApplicationConfig; + +namespace System.Windows.Forms.Generators.Tests +{ + public partial class ProjectFileReaderTests + { + private static readonly char s_separator = CultureInfo.CurrentCulture.TextInfo.ListSeparator[0]; + private static readonly dynamic s_static = typeof(ProjectFileReader).TestAccessor().Dynamic; + private readonly ITestOutputHelper _output; + + private static bool TryReadBool(AnalyzerConfigOptionsProvider configOptions, string propertyName, bool defaultValue, out bool value, out Diagnostic? diagnostic) + => (bool)s_static.TryReadBool(configOptions, propertyName, defaultValue, out value, out diagnostic); + + private static bool TryReadFont(AnalyzerConfigOptionsProvider configOptions, out FontDescriptor? font, out Diagnostic? diagnostic) + => (bool)s_static.TryReadFont(configOptions, out font, out diagnostic); + + private static bool TryReadHighDpiMode(AnalyzerConfigOptionsProvider configOptions, out HighDpiMode highDpiMode, out Diagnostic? diagnostic) + => (bool)s_static.TryReadHighDpiMode(configOptions, out highDpiMode, out diagnostic); + + public ProjectFileReaderTests(ITestOutputHelper output) + { + _output = output; + } + + [Theory] + [InlineData("a")] + [InlineData("@")] + [InlineData("yes")] + [InlineData("no")] + [InlineData("0")] + [InlineData("1")] + [InlineData("-1")] + public void ProjectFileReader_TryReadBool_value_invalid(string value) + { + Dictionary properties = new() + { + { $"build_property.myvalue", value } + }; + CompilerAnalyzerConfigOptions configOptions = new(properties.ToImmutableDictionary()); + CompilerAnalyzerConfigOptionsProvider provider = new(ImmutableDictionary.Empty, configOptions); + + bool result = TryReadBool(provider, "myvalue", defaultValue: false, out bool returnedValue, out Diagnostic? diagnostic); + + Assert.False(result); + Assert.False(returnedValue); + Assert.NotNull(diagnostic); + Assert.Equal(DiagnosticDescriptors.s_propertyCantBeSetToValue, diagnostic!.Descriptor); + _output.WriteLine(diagnostic.ToString()); + } + + [Theory] + [InlineData("", true)] // default value + [InlineData("false", false)] + [InlineData("true", true)] + [InlineData("False", false)] + [InlineData("True", true)] + [InlineData("FALSE", false)] + [InlineData("TRUE", true)] + public void ProjectFileReader_TryReadBool_value_valid(string value, bool expected) + { + Dictionary properties = new() + { + { $"build_property.myvalue", value } + }; + CompilerAnalyzerConfigOptions configOptions = new(properties.ToImmutableDictionary()); + CompilerAnalyzerConfigOptionsProvider provider = new(ImmutableDictionary.Empty, configOptions); + + bool result = TryReadBool(provider, "myvalue", defaultValue: true, out bool returnedValue, out Diagnostic? diagnostic); + + Assert.True(result); + Assert.Equal(expected, returnedValue); + Assert.Null(diagnostic); + } + + public static TheoryData ExceptionFontConverterData() + => new() + { + // ArgumentException + { $"Courier New{s_separator} 11 px{s_separator} type=Bold{s_separator} Italic" }, + { $"Courier New{s_separator} {s_separator} Style=Bold" }, + { $"Courier New{s_separator} 11{s_separator} Style=" }, + { $"Courier New{s_separator} 11{s_separator} Style=RandomEnum" }, + { $"Arial{s_separator} 10{s_separator} style=bold{s_separator}" }, + { $"Arial{s_separator} 10{s_separator} style=null" }, + { $"Arial{s_separator} 10{s_separator} style=abc#" }, + { $"Arial{s_separator} 10{s_separator} style=##" }, + { $"Arial{s_separator} 10display{s_separator} style=bold" }, + { $"Arial{s_separator} 10style{s_separator} style=bold" }, + + // InvalidEnumArgumentException + { $"Arial{s_separator} 10{s_separator} style=56" }, + { $"Arial{s_separator} 10{s_separator} style=-1" }, + }; + + [Theory] + [MemberData(nameof(ExceptionFontConverterData))] + public void ProjectFileReader_TryReadFont_value_invalid(string font) + { + Dictionary properties = new() + { + { $"build_property.{PropertyNameCSharp.DefaultFont}", font } + }; + CompilerAnalyzerConfigOptions configOptions = new(properties.ToImmutableDictionary()); + CompilerAnalyzerConfigOptionsProvider provider = new(ImmutableDictionary.Empty, configOptions); + + bool result = TryReadFont(provider, out FontDescriptor? returnedValue, out Diagnostic? diagnostic); + + Assert.False(result); + Assert.Null(returnedValue); + Assert.NotNull(diagnostic); + Assert.Equal(DiagnosticDescriptors.s_propertyCantBeSetToValueWithReason, diagnostic!.Descriptor); + _output.WriteLine(diagnostic.ToString()); + } + + public static TheoryData TestConvertFormData() + => new() + { + { "Courier New" }, + { $"Arial{s_separator} 11px" }, + { $"Courier New{s_separator} style=Bold" }, + { $"Courier New{s_separator} 11 px{s_separator} style=Regular, Italic" }, + { $"arIAL{s_separator} 10{s_separator} style=bold" }, + { $"Arial{s_separator}" }, + { $"{s_separator} 10{s_separator} style=bold" }, + + // NOTE: in .NET runtime these tests will result in FontName='', but the implementation relies on GDI+, which we don't have... + { $"11px{s_separator} Style=Bold" }, + { $"11px" }, + { $"Style=Bold" }, + }; + + [Theory] + [MemberData(nameof(TestConvertFormData))] + public void ProjectFileReader_TryReadFont_value_valid(string font) + { + Dictionary properties = new() + { + { $"build_property.{PropertyNameCSharp.DefaultFont}", font } + }; + CompilerAnalyzerConfigOptions configOptions = new(properties.ToImmutableDictionary()); + CompilerAnalyzerConfigOptionsProvider provider = new(ImmutableDictionary.Empty, configOptions); + + bool result = TryReadFont(provider, out _, out Diagnostic? diagnostic); + + Assert.True(result); + Assert.Null(diagnostic); + } + + [Theory] + [InlineData("@")] + [InlineData("yes")] + [InlineData("no")] + [InlineData("System")] + [InlineData("10")] + [InlineData("-1")] + [CommonMemberData(typeof(CommonTestHelper), nameof(CommonTestHelper.GetEnumTypeTheoryDataInvalid), typeof(HighDpiMode))] + public void ProjectFileReader_TryReadHighDpiMode_value_invalid(string value) + { + Dictionary properties = new() + { + { $"build_property.{PropertyNameCSharp.HighDpiMode}", value } + }; + CompilerAnalyzerConfigOptions configOptions = new(properties.ToImmutableDictionary()); + CompilerAnalyzerConfigOptionsProvider provider = new(ImmutableDictionary.Empty, configOptions); + + bool result = TryReadHighDpiMode(provider, out HighDpiMode returnedValue, out Diagnostic? diagnostic); + + Assert.False(result); + Assert.Equal(PropertyDefaultValue.DpiMode, returnedValue); + Assert.NotNull(diagnostic); + Assert.Equal(DiagnosticDescriptors.s_propertyCantBeSetToValue, diagnostic!.Descriptor); + _output.WriteLine(diagnostic.ToString()); + } + + [Theory] + [CommonMemberData(typeof(CommonTestHelper), nameof(CommonTestHelper.GetEnumTypeTheoryDataWitIdentity), typeof(HighDpiMode))] +#pragma warning disable xUnit1026 // Theory methods should use all of their parameters + public void ProjectFileReader_TryReadHighDpiMode_value_valid(string value, int counter) +#pragma warning restore xUnit1026 // Theory methods should use all of their parameters + { + Dictionary properties = new() + { + { $"build_property.{PropertyNameCSharp.HighDpiMode}", value } + }; + CompilerAnalyzerConfigOptions configOptions = new(properties.ToImmutableDictionary()); + CompilerAnalyzerConfigOptionsProvider provider = new(ImmutableDictionary.Empty, configOptions); + + bool result = TryReadHighDpiMode(provider, out HighDpiMode returnedValue, out Diagnostic? diagnostic); + + Assert.True(result); + Assert.Equal(Enum.Parse(value, true), returnedValue); + Assert.Null(diagnostic); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/TestAdditionalText.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/TestAdditionalText.cs new file mode 100644 index 00000000000..397808decf1 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/TestAdditionalText.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System.Diagnostics.CodeAnalysis; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace System.Windows.Forms.Analyzers.Tests +{ + // Borrowed from https://github.com/dotnet/roslyn/blob/main/src/Compilers/Test/Core/Mocks/TestAdditionalText.cs + + [ExcludeFromCodeCoverage] + public sealed class TestAdditionalText : AdditionalText + { + private readonly SourceText _text; + + public TestAdditionalText(string path, SourceText text) + { + Path = path; + _text = text; + } + + public TestAdditionalText(string text = "", Encoding? encoding = null, string path = "dummy") + : this(path, SourceText.From(text, encoding)) + { + } + + public override string Path { get; } + + public override SourceText GetText(CancellationToken cancellationToken = default) => _text; + } +} + diff --git a/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/Verifiers/CSharpSourceGeneratorTest`1.cs b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/Verifiers/CSharpSourceGeneratorTest`1.cs new file mode 100644 index 00000000000..583d02624ab --- /dev/null +++ b/src/System.Windows.Forms.Analyzers.CSharp/tests/UnitTests/Verifiers/CSharpSourceGeneratorTest`1.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.Verifiers; +using static Microsoft.CodeAnalysis.Testing.ReferenceAssemblies; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public class CSharpSourceGeneratorTest : CSharpSourceGeneratorTest + where TSourceGenerator : ISourceGenerator, new() + { + public CSharpSourceGeneratorTest() + { + ReferenceAssemblies = NetFramework.Net472.WindowsForms; + } + + /// + /// Allows you to specify additional global options that will appear in the context.AnalyzerConfigOptions.GlobalOptions object. + /// + public List<(string, string)> GlobalOptions { get; } = new(); + + protected override GeneratorDriver CreateGeneratorDriver(Project project, ImmutableArray sourceGenerators) + => CSharpGeneratorDriver.Create( + sourceGenerators, + project.AnalyzerOptions.AdditionalFiles, + (CSharpParseOptions)project.ParseOptions!, + new OptionsProvider(project.AnalyzerOptions.AnalyzerConfigOptionsProvider, GlobalOptions)); + + /// + /// This class just passes argument through to the projects options provider and it used to provider custom global options + /// + private class OptionsProvider : AnalyzerConfigOptionsProvider + { + private readonly AnalyzerConfigOptionsProvider _analyzerConfigOptionsProvider; + + public OptionsProvider(AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider, List<(string, string)> globalOptions) + { + _analyzerConfigOptionsProvider = analyzerConfigOptionsProvider; + GlobalOptions = new ConfigOptions(_analyzerConfigOptionsProvider.GlobalOptions, globalOptions); + } + + public override AnalyzerConfigOptions GlobalOptions { get; } + + public override AnalyzerConfigOptions GetOptions(SyntaxTree tree) + => _analyzerConfigOptionsProvider.GetOptions(tree); + + public override AnalyzerConfigOptions GetOptions(AdditionalText textFile) + => _analyzerConfigOptionsProvider.GetOptions(textFile); + } + + /// + /// Allows adding additional global options + /// + private class ConfigOptions : AnalyzerConfigOptions + { + private readonly AnalyzerConfigOptions _workspaceOptions; + private readonly Dictionary _globalOptions; + + public ConfigOptions(AnalyzerConfigOptions workspaceOptions, List<(string, string)> globalOptions) + { + _workspaceOptions = workspaceOptions; + _globalOptions = globalOptions.ToDictionary(t => t.Item1, t => t.Item2); + } + + public override bool TryGetValue(string key, [NotNullWhen(true)] out string? value) + => _workspaceOptions.TryGetValue(key, out value) || _globalOptions.TryGetValue(key, out value); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/AppManifestAnalyzer.Help.md b/src/System.Windows.Forms.Analyzers/AppManifestAnalyzer.Help.md new file mode 100644 index 00000000000..f80ac6db3fc --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/AppManifestAnalyzer.Help.md @@ -0,0 +1,2 @@ +# How to use AppManifestAnalyzer + diff --git a/src/System.Windows.Forms.Analyzers/src/AnalyzerReleases.Shipped.md b/src/System.Windows.Forms.Analyzers/src/AnalyzerReleases.Shipped.md new file mode 100644 index 00000000000..d567f14248e --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/AnalyzerReleases.Shipped.md @@ -0,0 +1,3 @@ +; Shipped analyzer releases +; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + diff --git a/src/System.Windows.Forms.Analyzers/src/AnalyzerReleases.Unshipped.md b/src/System.Windows.Forms.Analyzers/src/AnalyzerReleases.Unshipped.md new file mode 100644 index 00000000000..879a5225034 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/AnalyzerReleases.Unshipped.md @@ -0,0 +1,7 @@ +; Unshipped analyzer release +; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +### New Rules +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +WFAC010 | ApplicationConfiguration | Warning | AppManifestAnalyzer, [Documentation](https://github.com/dotnet/winforms/blob/main/src/System.Windows.Forms.Analyzers/AppManifestAnalyzer.Help.md) diff --git a/src/System.Windows.Forms.Analyzers/src/DiagnosticDescriptors.cs b/src/System.Windows.Forms.Analyzers/src/DiagnosticDescriptors.cs new file mode 100644 index 00000000000..a388210801a --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/DiagnosticDescriptors.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; + +namespace System.Windows.Forms.Analyzers +{ + internal static class DiagnosticDescriptors + { + private const string Category = "ApplicationConfiguration"; + + private static readonly LocalizableString s_localizableWFAC010Title + = new LocalizableResourceString(nameof(SR.WFAC010Title), SR.ResourceManager, typeof(SR)); + private static readonly LocalizableString s_localizableWFAC010Message_CS + = new LocalizableResourceString(nameof(SR.WFAC010Message_CS), SR.ResourceManager, typeof(SR)); + private static readonly LocalizableString s_localizableWFAC010Message_VB + = new LocalizableResourceString(nameof(SR.WFAC010Message_VB), SR.ResourceManager, typeof(SR)); + + internal static readonly DiagnosticDescriptor s_migrateHighDpiSettings_CSharp + = new(id: "WFAC010", + title: s_localizableWFAC010Title, + messageFormat: s_localizableWFAC010Message_CS, + category: Category, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + internal static readonly DiagnosticDescriptor s_migrateHighDpiSettings_VB + = new(id: "WFAC010", + title: s_localizableWFAC010Title, + messageFormat: s_localizableWFAC010Message_VB, + category: Category, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + } +} diff --git a/src/System.Windows.Forms.Analyzers/src/Properties/AssemblyInfo.cs b/src/System.Windows.Forms.Analyzers/src/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..4fb491f70cf --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("System.Windows.Forms.Analyzers.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/System.Windows.Forms.Analyzers/src/Properties/launchSettings.json b/src/System.Windows.Forms.Analyzers/src/Properties/launchSettings.json new file mode 100644 index 00000000000..ed455882673 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "System.Windows.Forms.Analyzers": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\..\\System.Windows.Forms\\tests\\IntegrationTests\\WinformsControlsTest\\WinformsControlsTest.csproj" + } + } +} \ No newline at end of file diff --git a/src/System.Windows.Forms.Analyzers/src/Resources/SR.resx b/src/System.Windows.Forms.Analyzers/src/Resources/SR.resx new file mode 100644 index 00000000000..7a282f6dddf --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/Resources/SR.resx @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Unsupported high DPI configuration + + + Remove high DPI settings from {0} and configure via Application.SetHighDpiMode API or '{1}' project property + + + Remove high DPI settings from {0} and configure via '{1}' property in Application Framework + + diff --git a/src/System.Windows.Forms.Analyzers/src/System.Windows.Forms.Analyzers.csproj b/src/System.Windows.Forms.Analyzers/src/System.Windows.Forms.Analyzers.csproj new file mode 100644 index 00000000000..f39b4bfce8b --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/System.Windows.Forms.Analyzers.csproj @@ -0,0 +1,36 @@ + + + + netstandard2.0 + Preview + enable + + true + + + true + WINFORMS_ANALYZERS + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + true + System + + + + diff --git a/src/System.Windows.Forms.Analyzers/src/System.Windows.Forms.Analyzers.props b/src/System.Windows.Forms.Analyzers/src/System.Windows.Forms.Analyzers.props new file mode 100644 index 00000000000..567a9a2bbe9 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/System.Windows.Forms.Analyzers.props @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/AnalyzerConfigOptionsProviderExtensions.cs b/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/AnalyzerConfigOptionsProviderExtensions.cs new file mode 100644 index 00000000000..92ecd1e4fb8 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/AnalyzerConfigOptionsProviderExtensions.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Diagnostics; + +namespace System.Windows.Forms.Analyzers +{ + internal static class AnalyzerConfigOptionsProviderExtensions + { + /// + /// Attempts to read a value for the requested MSBuild property. + /// + /// The global optins. + /// The name of the property to read the value for. + /// The property's value. + /// if the property is present; otherwise . + public static bool GetMSBuildProperty(this AnalyzerConfigOptionsProvider analyzerConfigOptions, string name, out string? value) + { + return analyzerConfigOptions.GlobalOptions.TryGetValue($"build_property.{name}", out value); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/AppManifestAnalyzer.cs b/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/AppManifestAnalyzer.cs new file mode 100644 index 00000000000..4dd0cc276e8 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/AppManifestAnalyzer.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Xml; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +namespace System.Windows.Forms.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public partial class AppManifestAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics + => ImmutableArray.Create(DiagnosticDescriptors.s_migrateHighDpiSettings_CSharp, + DiagnosticDescriptors.s_migrateHighDpiSettings_VB); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + context.EnableConcurrentExecution(); + + context.RegisterAdditionalFileAction(AdditionalFileAction); + } + + private void AdditionalFileAction(AdditionalFileAnalysisContext context) + { + if (context.AdditionalFile.Path.EndsWith(".manifest", StringComparison.OrdinalIgnoreCase)) + { + VerifyAppManifest(context, context.AdditionalFile); + } + + // TODO: look for app.config? + } + + private static void VerifyAppManifest(AdditionalFileAnalysisContext context, AdditionalText appManifest) + { + SourceText? appManifestXml = appManifest.GetText(context.CancellationToken); + if (appManifestXml is null) + { + return; + } + + // If the manifest file is corrupt - let the build fail + XmlDocument doc = new(); + try + { + doc.LoadXml(appManifestXml.ToString()); + } + catch + { + // Invalid xml, don't care + return; + } + + XmlNamespaceManager nsmgr = new(doc.NameTable); + nsmgr.AddNamespace("v1", "urn:schemas-microsoft-com:asm.v1"); + nsmgr.AddNamespace("v3", "urn:schemas-microsoft-com:asm.v3"); + nsmgr.AddNamespace("v3ws", "http://schemas.microsoft.com/SMI/2005/WindowsSettings"); + + if (doc.DocumentElement.SelectSingleNode("//v3:application/v3:windowsSettings/v3ws:dpiAware", nsmgr) is not null) + { + switch (context.Compilation.Language) + { + case LanguageNames.CSharp: + context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.s_migrateHighDpiSettings_CSharp, + Location.None, + appManifest.Path, + ApplicationConfig.PropertyNameCSharp.HighDpiMode)); + break; + case LanguageNames.VisualBasic: + context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.s_migrateHighDpiSettings_VB, + Location.None, + appManifest.Path, + ApplicationConfig.PropertyNameVisualBasic.HighDpiMode)); + break; + default: + throw new NotSupportedException(); + } + } + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/ApplicationConfig.FontDescriptor.cs b/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/ApplicationConfig.FontDescriptor.cs new file mode 100644 index 00000000000..c6b6d0cd44a --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/ApplicationConfig.FontDescriptor.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text.RegularExpressions; + +namespace System.Windows.Forms.Analyzers +{ + internal partial class ApplicationConfig + { + public class FontDescriptor + { + public FontDescriptor(string fontName, float emSize, FontStyle style, GraphicsUnit unit) + { + Name = fontName; + Size = emSize; + Style = style; + Unit = unit; + } + + public string Name { get; set; } + public float Size { get; } + public FontStyle Style { get; } + public GraphicsUnit Unit { get; } + + public override string ToString() + { + // Sanitize the name - remove anything but alpha-numeric and spaces. + string name = Regex.Replace(Name, @"[^\w\d ]", string.Empty); + + string fontFamily = string.IsNullOrWhiteSpace(name) + ? "Control.DefaultFont.FontFamily" + : $"new FontFamily(\"{name}\")"; + + return $"new Font({fontFamily}, {Size}f, (FontStyle){(int)Style}, (GraphicsUnit){(int)Unit})"; + } + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/ApplicationConfig.FontStyle.cs b/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/ApplicationConfig.FontStyle.cs new file mode 100644 index 00000000000..97877a077fa --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/ApplicationConfig.FontStyle.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Analyzers +{ + internal partial class ApplicationConfig + { + // Copied from https://github.com/dotnet/runtime/blob/00ee1c18715723e62484c9bc8a14f517455fc3b3/src/libraries/System.Drawing.Common/src/System/Drawing/FontStyle.cs + [Flags] + public enum FontStyle + { + /// + /// Normal text. + /// + Regular = 0, + /// + /// Bold text. + /// + Bold = 1, + /// + /// Italic text. + /// + Italic = 2, + /// + /// Underlined text. + /// + Underline = 4, + /// + /// Text with a line through the middle. + /// + Strikeout = 8, + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/ApplicationConfig.GraphicsUnit.cs b/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/ApplicationConfig.GraphicsUnit.cs new file mode 100644 index 00000000000..a436eb2a341 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/ApplicationConfig.GraphicsUnit.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Analyzers +{ + internal partial class ApplicationConfig + { + // Copied from https://github.com/dotnet/runtime/blob/00ee1c18715723e62484c9bc8a14f517455fc3b3/src/libraries/System.Drawing.Common/src/System/Drawing/GraphicsUnit.cs + public enum GraphicsUnit + { + /// + /// Specifies the world unit as the unit of measure. + /// + World = 0, + /// + /// Specifies 1/75 inch as the unit of measure. + /// + Display = 1, + /// + /// Specifies a device pixel as the unit of measure. + /// + Pixel = 2, + /// + /// Specifies a printer's point (1/72 inch) as the unit of measure. + /// + Point = 3, + /// + /// Specifies the inch as the unit of measure. + /// + Inch = 4, + /// + /// Specifies the document unit (1/300 inch) as the unit of measure. + /// + Document = 5, + /// + /// Specifies the millimeter as the unit of measure. + /// + Millimeter = 6 + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/ApplicationConfig.cs b/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/ApplicationConfig.cs new file mode 100644 index 00000000000..a5a01c309c4 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/src/System/Windows/Forms/ApplicationConfig.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Analyzers +{ + internal partial class ApplicationConfig + { + public static class PropertyNameCSharp + { + public const string EnableVisualStyles = "ApplicationVisualStyles"; + public const string DefaultFont = "ApplicationDefaultFont"; + public const string HighDpiMode = "ApplicationHighDpiMode"; + public const string UseCompatibleTextRendering = "ApplicationUseCompatibleTextRendering"; + } + + public static class PropertyNameVisualBasic + { + public const string EnableVisualStyles = "EnableVisualStyles"; + public const string HighDpiMode = "HighDpiMode"; + } + + public static class PropertyDefaultValue + { + public const bool EnableVisualStyles = true; + public const float FontSize = 9f; + public const HighDpiMode DpiMode = HighDpiMode.PerMonitorV2; + public const bool UseCompatibleTextRendering = false; + } + + public bool EnableVisualStyles { get; set; } + public FontDescriptor? DefaultFont { get; set; } + public HighDpiMode HighDpiMode { get; set; } + public bool UseCompatibleTextRendering { get; set; } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/System.Windows.Forms.Analyzers.Tests.csproj b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System.Windows.Forms.Analyzers.Tests.csproj new file mode 100644 index 00000000000..60cd05a24b5 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System.Windows.Forms.Analyzers.Tests.csproj @@ -0,0 +1,38 @@ + + + + enable + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + Always + + + diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/AppManifestAnalyzerTests.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/AppManifestAnalyzerTests.cs new file mode 100644 index 00000000000..b012d38be69 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/AppManifestAnalyzerTests.cs @@ -0,0 +1,143 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; +using Xunit; +using VerifyCS = System.Windows.Forms.Analyzers.Tests.CSharpAnalyzerVerifier< + System.Windows.Forms.Analyzers.AppManifestAnalyzer>; +using VerifyVB = System.Windows.Forms.Analyzers.Tests.VisualBasicAnalyzerVerifier< + System.Windows.Forms.Analyzers.AppManifestAnalyzer>; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public class AppManifestAnalyzerTests + { + private const string CSharCode = @" + namespace ConsoleApplication1 + { + class {|#0:TypeName|} + { + } + }"; + private const string VbCode = @" +Namespace ConsoleApplication1 + Class {|#0:TypeName|} + End Class +End Namespace"; + + [Fact] + public async Task AppManifestAnalyzer_noop_if_no_manifest_file() + { + await new VerifyCS.Test + { + TestCode = CSharCode, + TestState = + { + AdditionalFiles = { } + } + }.RunAsync(); + } + + [Fact] + public async Task AppManifestAnalyzer_noop_if_manifest_file_has_no_dpi_info() + { + SourceText manifestFile = SourceText.From(File.ReadAllText(@"System\Windows\Forms\Analyzers\MockData\nodpi.manifest")); + await new VerifyCS.Test + { + TestCode = CSharCode, + TestState = + { + AdditionalFiles = { (@"C:\temp\app.manifest", manifestFile) } + } + }.RunAsync(); + } + + [Fact] + public async Task AppManifestAnalyzer_noop_if_manifest_file_corrupt() + { + SourceText manifestFile = SourceText.From(File.ReadAllText(@"System\Windows\Forms\Analyzers\MockData\invalid.manifest")); + await new VerifyCS.Test + { + TestCode = CSharCode, + TestState = + { + AdditionalFiles = { (@"C:\temp\app.manifest", manifestFile) } + } + }.RunAsync(); + } + + [Fact] + public async Task AppManifestAnalyzer_warn_if_manifest_file_has_dpi_info_CSharp() + { + const string manifestFilePath = @"C:\temp\app.manifest"; + SourceText manifestFile = SourceText.From(File.ReadAllText(@"System\Windows\Forms\Analyzers\MockData\dpi.manifest")); + await new VerifyCS.Test + { + TestCode = CSharCode, + TestState = + { + AdditionalFiles = { (manifestFilePath, manifestFile) } + }, + ExpectedDiagnostics = + { + new DiagnosticResult(DiagnosticDescriptors.s_migrateHighDpiSettings_CSharp) + .WithArguments(manifestFilePath, ApplicationConfig.PropertyNameCSharp.HighDpiMode) + } + }.RunAsync(); + } + + [Fact] + public async Task AppManifestAnalyzer_warn_if_manifest_file_has_dpi_info_VB() + { + const string manifestFilePath = @"C:\temp\app.manifest"; + SourceText manifestFile = SourceText.From(File.ReadAllText(@"System\Windows\Forms\Analyzers\MockData\dpi.manifest")); + await new VerifyVB.Test + { + TestCode = VbCode, + TestState = + { + AdditionalFiles = { (manifestFilePath, manifestFile) } + }, + ExpectedDiagnostics = + { + new DiagnosticResult(DiagnosticDescriptors.s_migrateHighDpiSettings_VB) + .WithArguments(manifestFilePath, ApplicationConfig.PropertyNameVisualBasic.HighDpiMode) + } + }.RunAsync(); + } + + [Fact] + public async Task AppManifestAnalyzer_can_suppressed_if_manifest_file_has_dpi_info_CSharp() + { + const string manifestFilePath = @"C:\temp\app.manifest"; + SourceText manifestFile = SourceText.From(File.ReadAllText(@"System\Windows\Forms\Analyzers\MockData\dpi.manifest")); + await new VerifyCS.Test + { + TestCode = CSharCode, + TestState = + { + AdditionalFiles = { (manifestFilePath, manifestFile) }, + AnalyzerConfigFiles = { ("/.globalconfig", $"is_global = true\r\ndotnet_diagnostic.WFAC010.severity = none") } + } + }.RunAsync(); + } + + [Fact] + public async Task AppManifestAnalyzer_can_suppressed_if_manifest_file_has_dpi_info_VB() + { + const string manifestFilePath = @"C:\temp\app.manifest"; + SourceText manifestFile = SourceText.From(File.ReadAllText(@"System\Windows\Forms\Analyzers\MockData\dpi.manifest")); + await new VerifyVB.Test + { + TestCode = VbCode, + TestState = + { + AdditionalFiles = { (manifestFilePath, manifestFile) }, + AnalyzerConfigFiles = { ("/.globalconfig", $"is_global = true\r\ndotnet_diagnostic.WFAC010.severity = none") } + } + }.RunAsync(); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/ApplicationConfigTests.FontDescriptor.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/ApplicationConfigTests.FontDescriptor.cs new file mode 100644 index 00000000000..64573d0bdd5 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/ApplicationConfigTests.FontDescriptor.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; +using Xunit.Abstractions; +using static System.Windows.Forms.Analyzers.ApplicationConfig; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public partial class ApplicationConfigTests + { + public class FontDescriptorTests + { + private readonly ITestOutputHelper _output; + + public FontDescriptorTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void FontDescriptor_ctor() + { + FontDescriptor descriptor = new("fontName", 10f, FontStyle.Bold | FontStyle.Italic, GraphicsUnit.Point); + + Assert.Equal("fontName", descriptor.Name); + Assert.Equal(10f, descriptor.Size); + Assert.Equal(FontStyle.Bold | FontStyle.Italic, descriptor.Style); + Assert.Equal(GraphicsUnit.Point, descriptor.Unit); + } + + [Theory] + [InlineData("", "new Font(Control.DefaultFont.FontFamily, 10f, (FontStyle)3, (GraphicsUnit)3)")] + [InlineData(" ", "new Font(Control.DefaultFont.FontFamily, 10f, (FontStyle)3, (GraphicsUnit)3)")] + [InlineData("\t", "new Font(Control.DefaultFont.FontFamily, 10f, (FontStyle)3, (GraphicsUnit)3)")] + [InlineData("fontName", "new Font(new FontFamily(\"fontName\"), 10f, (FontStyle)3, (GraphicsUnit)3)")] + [InlineData("\"fontName\"", "new Font(new FontFamily(\"fontName\"), 10f, (FontStyle)3, (GraphicsUnit)3)")] + [InlineData("Name with \tspaces", "new Font(new FontFamily(\"Name with spaces\"), 10f, (FontStyle)3, (GraphicsUnit)3)")] + [InlineData("Name with 'quotes'", "new Font(new FontFamily(\"Name with quotes\"), 10f, (FontStyle)3, (GraphicsUnit)3)")] + [InlineData("Name with \r\n lines", "new Font(new FontFamily(\"Name with lines\"), 10f, (FontStyle)3, (GraphicsUnit)3)")] + public void FontDescriptor_ToString(string fontName, string expected) + { + FontDescriptor descriptor = new(fontName, 10f, FontStyle.Bold | FontStyle.Italic, GraphicsUnit.Point); + + _output.WriteLine(descriptor.ToString()); + Assert.Equal(expected, descriptor.ToString()); + } + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/ApplicationConfigTests.FontStyle.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/ApplicationConfigTests.FontStyle.cs new file mode 100644 index 00000000000..b38698f6d94 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/ApplicationConfigTests.FontStyle.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; +using static System.Windows.Forms.Analyzers.ApplicationConfig; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public partial class ApplicationConfigTests + { + public class FontStyleTests + { + [Fact] + public void GetStandardValuesTest() + { + var values = Enum.GetValues(typeof(FontStyle)); + Assert.Equal(5, values.Length); // The values of Graphics unit: Regular, Bold, Italic, Underline, Strikeout. + } + + [Theory] + [InlineData("Bold", FontStyle.Bold)] + [InlineData("Italic", FontStyle.Italic)] + [InlineData("Regular", FontStyle.Regular)] + [InlineData("Strikeout", FontStyle.Strikeout)] + [InlineData("Underline", FontStyle.Underline)] + internal void CanConvertFrom(string input, FontStyle expected) + { + FontStyle value = Enum.Parse(input); + Assert.Equal(expected, value); + } + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/ApplicationConfigTests.GraphicsUnit.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/ApplicationConfigTests.GraphicsUnit.cs new file mode 100644 index 00000000000..5c683f9291b --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/ApplicationConfigTests.GraphicsUnit.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; +using static System.Windows.Forms.Analyzers.ApplicationConfig; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public partial class ApplicationConfigTests + { + // Copied from https://github.com/dotnet/runtime/blob/00ee1c18715723e62484c9bc8a14f517455fc3b3/src/libraries/System.Drawing.Common/tests/System/Drawing/FontConverterTests.cs#L203 + public class GraphicsUnitTests + { + [Fact] + public void GetStandardValuesTest() + { + var values = Enum.GetValues(typeof(GraphicsUnit)); + Assert.Equal(7, values.Length); // The values of Graphics unit: World, Display, Pixel, Point, Inch, Document, Millimeter. + } + + [Theory] + [InlineData("Display", GraphicsUnit.Display)] + [InlineData("Document", GraphicsUnit.Document)] + [InlineData("Inch", GraphicsUnit.Inch)] + [InlineData("Millimeter", GraphicsUnit.Millimeter)] + [InlineData("Pixel", GraphicsUnit.Pixel)] + [InlineData("Point", GraphicsUnit.Point)] + [InlineData("World", GraphicsUnit.World)] + internal void CanConvertFrom(string input, GraphicsUnit expected) + { + GraphicsUnit value = Enum.Parse(input); + Assert.Equal(expected, value); + } + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/ApplicationConfigTests.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/ApplicationConfigTests.cs new file mode 100644 index 00000000000..c5193d493d1 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/ApplicationConfigTests.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms.Analyzers.Tests +{ + public partial class ApplicationConfigTests + { + // See nested files + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/MockData/dpi.manifest b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/MockData/dpi.manifest new file mode 100644 index 00000000000..ef80698dfef --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/MockData/dpi.manifest @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/MockData/invalid.manifest b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/MockData/invalid.manifest new file mode 100644 index 00000000000..e09f1115699 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/MockData/invalid.manifest @@ -0,0 +1,45 @@ + + + + + + + + + + + + + diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/MockData/nodpi.manifest b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/MockData/nodpi.manifest new file mode 100644 index 00000000000..d9bbc1e5f7b --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/System/Windows/Forms/Analyzers/MockData/nodpi.manifest @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpAnalyzerVerifier`1+Test.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpAnalyzerVerifier`1+Test.cs new file mode 100644 index 00000000000..812850ee2dc --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpAnalyzerVerifier`1+Test.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Testing.Verifiers; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public static partial class CSharpAnalyzerVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + { + public class Test : CSharpAnalyzerTest + { + public Test() + { + ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net472.WindowsForms; + + SolutionTransforms.Add((solution, projectId) => + { + CompilationOptions compilationOptions = solution.GetProject(projectId)!.CompilationOptions!; + compilationOptions = compilationOptions.WithSpecificDiagnosticOptions( + compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings)); + solution = solution.WithProjectCompilationOptions(projectId, compilationOptions); + + return solution; + }); + } + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpAnalyzerVerifier`1.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpAnalyzerVerifier`1.cs new file mode 100644 index 00000000000..cde85617ab4 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpAnalyzerVerifier`1.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Testing.Verifiers; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public static partial class CSharpAnalyzerVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + { + /// + public static DiagnosticResult Diagnostic() + => CSharpAnalyzerVerifier.Diagnostic(); + + /// + public static DiagnosticResult Diagnostic(string diagnosticId) + => CSharpAnalyzerVerifier.Diagnostic(diagnosticId); + + /// + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => CSharpAnalyzerVerifier.Diagnostic(descriptor); + + /// + public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs new file mode 100644 index 00000000000..2b387ad0161 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpCodeFixVerifier`2+Test.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Testing.Verifiers; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public static partial class CSharpCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + public class Test : CSharpCodeFixTest + { + public Test() + { + ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net472.WindowsForms; + + SolutionTransforms.Add((solution, projectId) => + { + CompilationOptions? compilationOptions = solution.GetProject(projectId)!.CompilationOptions!; + compilationOptions = compilationOptions.WithSpecificDiagnosticOptions( + compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings)); + solution = solution.WithProjectCompilationOptions(projectId, compilationOptions); + + return solution; + }); + } + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs new file mode 100644 index 00000000000..9bfeec036d5 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Testing.Verifiers; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public static partial class CSharpCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + /// + public static DiagnosticResult Diagnostic() + => CSharpCodeFixVerifier.Diagnostic(); + + /// + public static DiagnosticResult Diagnostic(string diagnosticId) + => CSharpCodeFixVerifier.Diagnostic(diagnosticId); + + /// + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => CSharpCodeFixVerifier.Diagnostic(descriptor); + + /// + public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + + /// + public static async Task VerifyCodeFixAsync(string source, string fixedSource) + => await VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + + /// + public static async Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) + => await VerifyCodeFixAsync(source, new[] { expected }, fixedSource); + + /// + public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpCodeRefactoringVerifier`1+Test.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpCodeRefactoringVerifier`1+Test.cs new file mode 100644 index 00000000000..a31210b52ff --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpCodeRefactoringVerifier`1+Test.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Testing.Verifiers; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public static partial class CSharpCodeRefactoringVerifier + where TCodeRefactoring : CodeRefactoringProvider, new() + { + public class Test : CSharpCodeRefactoringTest + { + public Test() + { + ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net472.WindowsForms; + + SolutionTransforms.Add((solution, projectId) => + { + CompilationOptions compilationOptions = solution.GetProject(projectId)!.CompilationOptions!; + compilationOptions = compilationOptions.WithSpecificDiagnosticOptions( + compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings)); + solution = solution.WithProjectCompilationOptions(projectId, compilationOptions); + + return solution; + }); + } + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpCodeRefactoringVerifier`1.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpCodeRefactoringVerifier`1.cs new file mode 100644 index 00000000000..e1840ae682d --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpCodeRefactoringVerifier`1.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Testing; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public static partial class CSharpCodeRefactoringVerifier + where TCodeRefactoring : CodeRefactoringProvider, new() + { + /// + public static async Task VerifyRefactoringAsync(string source, string fixedSource) + { + await VerifyRefactoringAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + } + + /// + public static async Task VerifyRefactoringAsync(string source, DiagnosticResult expected, string fixedSource) + { + await VerifyRefactoringAsync(source, new[] { expected }, fixedSource); + } + + /// + public static async Task VerifyRefactoringAsync(string source, DiagnosticResult[] expected, string fixedSource) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpVerifierHelper.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpVerifierHelper.cs new file mode 100644 index 00000000000..59157b253fc --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/CSharpVerifierHelper.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using System.Collections.Immutable; + +namespace System.Windows.Forms.Analyzers.Tests +{ + internal static class CSharpVerifierHelper + { + /// + /// By default, the compiler reports diagnostics for nullable reference types at + /// , and the analyzer test framework defaults to only validating + /// diagnostics at . This map contains all compiler diagnostic IDs + /// related to nullability mapped to , which is then used to enable all + /// of these warnings for default validation during analyzer and code fix tests. + /// + internal static ImmutableDictionary NullableWarnings { get; } = GetNullableWarningsFromCompiler(); + + private static ImmutableDictionary GetNullableWarningsFromCompiler() + { + string[] args = { "/warnaserror:nullable" }; + var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory); + var nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions; + + // Workaround for https://github.com/dotnet/roslyn/issues/41610 + nullableWarnings = nullableWarnings + .SetItem("CS8632", ReportDiagnostic.Error) + .SetItem("CS8669", ReportDiagnostic.Error); + + return nullableWarnings; + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicAnalyzerVerifier`1+Test.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicAnalyzerVerifier`1+Test.cs new file mode 100644 index 00000000000..7c1e9509eec --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicAnalyzerVerifier`1+Test.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Testing.Verifiers; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public static partial class VisualBasicAnalyzerVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + { + public class Test : VisualBasicAnalyzerTest + { + public Test() + { + ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net472.WindowsForms; + } + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicAnalyzerVerifier`1.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicAnalyzerVerifier`1.cs new file mode 100644 index 00000000000..2b1600d039f --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicAnalyzerVerifier`1.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Testing.Verifiers; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public static partial class VisualBasicAnalyzerVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + { + /// + public static DiagnosticResult Diagnostic() + => VisualBasicAnalyzerVerifier.Diagnostic(); + + /// + public static DiagnosticResult Diagnostic(string diagnosticId) + => VisualBasicAnalyzerVerifier.Diagnostic(diagnosticId); + + /// + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => VisualBasicAnalyzerVerifier.Diagnostic(descriptor); + + /// + public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicCodeFixVerifier`2+Test.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicCodeFixVerifier`2+Test.cs new file mode 100644 index 00000000000..16080c2d472 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicCodeFixVerifier`2+Test.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.Verifiers; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public static partial class VisualBasicCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + public class Test : VisualBasicCodeFixTest + { + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicCodeFixVerifier`2.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicCodeFixVerifier`2.cs new file mode 100644 index 00000000000..980d4282f2a --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicCodeFixVerifier`2.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Testing.Verifiers; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public static partial class VisualBasicCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + /// + public static DiagnosticResult Diagnostic() + => VisualBasicCodeFixVerifier.Diagnostic(); + + /// + public static DiagnosticResult Diagnostic(string diagnosticId) + => VisualBasicCodeFixVerifier.Diagnostic(diagnosticId); + + /// + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => VisualBasicCodeFixVerifier.Diagnostic(descriptor); + + /// + public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + + /// + public static async Task VerifyCodeFixAsync(string source, string fixedSource) + => await VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + + /// + public static async Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) + => await VerifyCodeFixAsync(source, new[] { expected }, fixedSource); + + /// + public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicCodeRefactoringVerifier`1+Test.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicCodeRefactoringVerifier`1+Test.cs new file mode 100644 index 00000000000..b488647ce89 --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicCodeRefactoringVerifier`1+Test.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Testing.Verifiers; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public static partial class VisualBasicCodeRefactoringVerifier + where TCodeRefactoring : CodeRefactoringProvider, new() + { + public class Test : VisualBasicCodeRefactoringTest + { + } + } +} diff --git a/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicCodeRefactoringVerifier`1.cs b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicCodeRefactoringVerifier`1.cs new file mode 100644 index 00000000000..c6d9240dbff --- /dev/null +++ b/src/System.Windows.Forms.Analyzers/tests/UnitTests/Verifiers/VisualBasicCodeRefactoringVerifier`1.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Testing; + +namespace System.Windows.Forms.Analyzers.Tests +{ + public static partial class VisualBasicCodeRefactoringVerifier + where TCodeRefactoring : CodeRefactoringProvider, new() + { + /// + public static async Task VerifyRefactoringAsync(string source, string fixedSource) + { + await VerifyRefactoringAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + } + + /// + public static async Task VerifyRefactoringAsync(string source, DiagnosticResult expected, string fixedSource) + { + await VerifyRefactoringAsync(source, new[] { expected }, fixedSource); + } + + /// + public static async Task VerifyRefactoringAsync(string source, DiagnosticResult[] expected, string fixedSource) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + } +} diff --git a/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/HighDpiMode.cs b/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/HighDpiMode.cs index 06028daacd9..e0c0950b25d 100644 --- a/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/HighDpiMode.cs +++ b/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/HighDpiMode.cs @@ -2,7 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if WINFORMS_ANALYZERS +namespace System.Windows.Forms.Analyzers +#else namespace System.Windows.Forms +#endif { /// /// Specifies the different high DPI modes that can be applied to an application. diff --git a/src/System.Windows.Forms.PrivateSourceGenerators/src/System.Windows.Forms.PrivateSourceGenerators.csproj b/src/System.Windows.Forms.PrivateSourceGenerators/src/System.Windows.Forms.PrivateSourceGenerators.csproj index 62eb690b7f6..a973da9f153 100644 --- a/src/System.Windows.Forms.PrivateSourceGenerators/src/System.Windows.Forms.PrivateSourceGenerators.csproj +++ b/src/System.Windows.Forms.PrivateSourceGenerators/src/System.Windows.Forms.PrivateSourceGenerators.csproj @@ -1,9 +1,10 @@ - + netstandard2.0 Preview enable + $(NoWarn);RS1024 diff --git a/src/System.Windows.Forms.PrivateSourceGenerators/tests/UnitTests/System.Windows.Forms.PrivateSourceGenerators.Tests.csproj b/src/System.Windows.Forms.PrivateSourceGenerators/tests/UnitTests/System.Windows.Forms.PrivateSourceGenerators.Tests.csproj index bf32c849168..208a5519844 100644 --- a/src/System.Windows.Forms.PrivateSourceGenerators/tests/UnitTests/System.Windows.Forms.PrivateSourceGenerators.Tests.csproj +++ b/src/System.Windows.Forms.PrivateSourceGenerators/tests/UnitTests/System.Windows.Forms.PrivateSourceGenerators.Tests.csproj @@ -1,10 +1,5 @@  - - System.Windows.Forms.PrivateSourceGenerators.Tests - true - - diff --git a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/Program.cs b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/Program.cs index 9897a4187df..c8c70ec5c0d 100644 --- a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/Program.cs +++ b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/Program.cs @@ -2,39 +2,22 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Drawing; using System.Windows.Forms; +using WinformsControlsTest; -namespace WinformsControlsTest -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - Application.EnableVisualStyles(); - Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); - - //Application.SetDefaultFont(new Font(new FontFamily("Microsoft Sans Serif"), 8f)); - //Application.SetDefaultFont(new Font(new FontFamily("Chiller"), 12f)); - Application.SetDefaultFont(new Font(new FontFamily("Calibri"), 11f)); +ApplicationConfiguration.Initialize(); - Application.SetCompatibleTextRenderingDefault(false); - Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException); //UnhandledExceptionMode.ThrowException - Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; - try - { - Application.Run(new MainForm()); - } - catch (System.Exception) - { - Environment.Exit(-1); - } +Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException); +Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; - Environment.Exit(0); - } - } +try +{ + Application.Run(new MainForm()); } +catch (Exception) +{ + Environment.Exit(-1); +} + +Environment.Exit(0); + diff --git a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/WinformsControlsTest.csproj b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/WinformsControlsTest.csproj index 6e862d82412..7eaa87a7fb6 100644 --- a/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/WinformsControlsTest.csproj +++ b/src/System.Windows.Forms/tests/IntegrationTests/WinformsControlsTest/WinformsControlsTest.csproj @@ -12,10 +12,6 @@ true - - NETCORE - - @@ -23,7 +19,6 @@ - WinformsControlsTest.SmallA.bmp @@ -53,4 +48,21 @@ + + + + false + + + + + + + + +