diff --git a/Avalonia.sln b/Avalonia.sln
index a792774d94..5043f474c2 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -117,6 +117,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\Base.props = build\Base.props
build\Binding.props = build\Binding.props
build\CoreLibraries.props = build\CoreLibraries.props
+ build\DevAnalyzers.props = build\DevAnalyzers.props
build\EmbedXaml.props = build\EmbedXaml.props
build\HarfBuzzSharp.props = build\HarfBuzzSharp.props
build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
@@ -234,7 +235,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -2238,6 +2241,30 @@ Global
{CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhone.Build.0 = Release|Any CPU
{CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.Build.0 = Release|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2303,6 +2330,7 @@ Global
{A0D0A6A4-5C72-4ADA-9B27-621C7D94F270} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{CE910927-CE5A-456F-BC92-E4C757354A5C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+ {2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/build/DevAnalyzers.props b/build/DevAnalyzers.props
new file mode 100644
index 0000000000..28959dbd47
--- /dev/null
+++ b/build/DevAnalyzers.props
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/src/Android/Avalonia.Android/Avalonia.Android.csproj b/src/Android/Avalonia.Android/Avalonia.Android.csproj
index 203c3accd6..ad27888f26 100644
--- a/src/Android/Avalonia.Android/Avalonia.Android.csproj
+++ b/src/Android/Avalonia.Android/Avalonia.Android.csproj
@@ -16,4 +16,5 @@
+
diff --git a/src/Avalonia.Animation/Avalonia.Animation.csproj b/src/Avalonia.Animation/Avalonia.Animation.csproj
index d81d14bbff..80726f2884 100644
--- a/src/Avalonia.Animation/Avalonia.Animation.csproj
+++ b/src/Avalonia.Animation/Avalonia.Animation.csproj
@@ -11,4 +11,5 @@
+
diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index 376075df2a..637b86d759 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -12,4 +12,5 @@
+
diff --git a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
index c89157dc0f..0438e4e0e8 100644
--- a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
+++ b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
@@ -23,4 +23,5 @@
+
diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj
index 543a513d57..5d34261bdf 100644
--- a/src/Avalonia.Controls/Avalonia.Controls.csproj
+++ b/src/Avalonia.Controls/Avalonia.Controls.csproj
@@ -19,4 +19,5 @@
+
diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
index 1fc3604f70..bb908e07dc 100644
--- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
+++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
@@ -34,4 +34,5 @@
+
diff --git a/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj b/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj
index 770d15ea27..a311efdfb0 100644
--- a/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj
+++ b/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj
@@ -15,4 +15,5 @@
+
diff --git a/src/Avalonia.Input/Avalonia.Input.csproj b/src/Avalonia.Input/Avalonia.Input.csproj
index e66d1d7e7c..6e0321b3aa 100644
--- a/src/Avalonia.Input/Avalonia.Input.csproj
+++ b/src/Avalonia.Input/Avalonia.Input.csproj
@@ -15,4 +15,5 @@
+
diff --git a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
index 03a53f7d86..fa115ed980 100644
--- a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
+++ b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
@@ -11,4 +11,5 @@
+
\ No newline at end of file
diff --git a/src/Avalonia.Layout/Avalonia.Layout.csproj b/src/Avalonia.Layout/Avalonia.Layout.csproj
index f2c4b0540b..d94c3eb101 100644
--- a/src/Avalonia.Layout/Avalonia.Layout.csproj
+++ b/src/Avalonia.Layout/Avalonia.Layout.csproj
@@ -10,4 +10,5 @@
+
diff --git a/src/Avalonia.Native/Avalonia.Native.csproj b/src/Avalonia.Native/Avalonia.Native.csproj
index 70ab38e786..2001a2fcbc 100644
--- a/src/Avalonia.Native/Avalonia.Native.csproj
+++ b/src/Avalonia.Native/Avalonia.Native.csproj
@@ -28,4 +28,6 @@
+
+
diff --git a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj
index 2b32926008..07cd295711 100644
--- a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj
+++ b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj
@@ -9,5 +9,7 @@
-
+
+
+
diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj
index 139ba1bd2e..260cfb0516 100644
--- a/src/Avalonia.Styling/Avalonia.Styling.csproj
+++ b/src/Avalonia.Styling/Avalonia.Styling.csproj
@@ -10,4 +10,5 @@
+
diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj
index a0b1f99fa1..7e9947812b 100644
--- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj
+++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj
@@ -19,4 +19,5 @@
+
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
index f75c96c759..b89ea8399a 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
@@ -12,5 +12,6 @@
-
+
+
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index 548aae31a8..6624392258 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -68,4 +68,5 @@
+
diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
index 21370b3bf5..119f57973c 100644
--- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
+++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj
@@ -18,4 +18,5 @@
+
diff --git a/src/Shared/ModuleInitializer.cs b/src/Shared/ModuleInitializer.cs
index c14b150c1a..b5bb31ed81 100644
--- a/src/Shared/ModuleInitializer.cs
+++ b/src/Shared/ModuleInitializer.cs
@@ -3,7 +3,7 @@ namespace System.Runtime.CompilerServices
#if !NET5_0_OR_GREATER
internal class ModuleInitializerAttribute : Attribute
{
-
+
}
#endif
}
diff --git a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj
index 50ace8209c..26c0fb756e 100644
--- a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj
+++ b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj
@@ -16,5 +16,6 @@
-
+
+
diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
index a1d8a60cb9..bd1f05b30f 100644
--- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
+++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
@@ -18,4 +18,5 @@
+
diff --git a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj
index 9f49808422..cc8a40e2d4 100644
--- a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj
+++ b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj
@@ -18,4 +18,5 @@
+
diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
index ba842757d7..b9a1880435 100644
--- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
+++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
@@ -16,5 +16,6 @@
-
+
+
diff --git a/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj b/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj
index 39a0305b1e..4e134b7d4e 100644
--- a/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj
+++ b/src/iOS/Avalonia.iOS/Avalonia.iOS.csproj
@@ -19,4 +19,6 @@
+
+
diff --git a/src/tools/DevAnalyzers/DevAnalyzers.csproj b/src/tools/DevAnalyzers/DevAnalyzers.csproj
new file mode 100644
index 0000000000..53e3e74e76
--- /dev/null
+++ b/src/tools/DevAnalyzers/DevAnalyzers.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netstandard2.0
+ 10
+ enable
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
diff --git a/src/tools/DevAnalyzers/GenericVirtualAnalyzer.cs b/src/tools/DevAnalyzers/GenericVirtualAnalyzer.cs
new file mode 100644
index 0000000000..8ecd9119f6
--- /dev/null
+++ b/src/tools/DevAnalyzers/GenericVirtualAnalyzer.cs
@@ -0,0 +1,40 @@
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace DevAnalyzers;
+
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public class GenericVirtualAnalyzer : DiagnosticAnalyzer
+{
+ public const string DiagnosticId = "AVADEV1001";
+
+ private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
+ DiagnosticId,
+ "Do not use generic virtual methods",
+ "Method '{0}' is a generic virtual method",
+ "Performance",
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: "Generic virtual methods affect JIT startup time adversly and should be avoided.");
+
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);
+ }
+
+ private static void AnalyzeMethod(SymbolAnalysisContext context)
+ {
+ var symbol = (IMethodSymbol)context.Symbol;
+
+ if (symbol.IsGenericMethod &&
+ (symbol.IsVirtual || symbol.ContainingType.TypeKind == TypeKind.Interface))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(Rule, symbol.Locations[0], symbol.Name));
+ }
+ }
+}
diff --git a/src/tools/DevAnalyzers/GlobalSuppressions.cs b/src/tools/DevAnalyzers/GlobalSuppressions.cs
new file mode 100644
index 0000000000..9428b904b8
--- /dev/null
+++ b/src/tools/DevAnalyzers/GlobalSuppressions.cs
@@ -0,0 +1,8 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008:Enable analyzer release tracking")]