diff --git a/.editorconfig b/.editorconfig
index a144ec8843..d07618df6c 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -186,6 +186,23 @@ csharp_wrap_before_ternary_opsigns = false
# Avalonia DevAnalyzer preferences
dotnet_diagnostic.AVADEV2001.severity = error
+# Avalonia PublicAnalyzer preferences
+dotnet_diagnostic.AVP1000.severity = error
+dotnet_diagnostic.AVP1001.severity = error
+dotnet_diagnostic.AVP1002.severity = error
+dotnet_diagnostic.AVP1010.severity = error
+dotnet_diagnostic.AVP1011.severity = error
+dotnet_diagnostic.AVP1012.severity = warning
+dotnet_diagnostic.AVP1013.severity = error
+dotnet_diagnostic.AVP1020.severity = error
+dotnet_diagnostic.AVP1021.severity = error
+dotnet_diagnostic.AVP1022.severity = error
+dotnet_diagnostic.AVP1030.severity = error
+dotnet_diagnostic.AVP1031.severity = error
+dotnet_diagnostic.AVP1032.severity = error
+dotnet_diagnostic.AVP1040.severity = error
+dotnet_diagnostic.AVA2001.severity = error
+
# Xaml files
[*.{xaml,axaml}]
indent_size = 2
diff --git a/Avalonia.sln b/Avalonia.sln
index d4ccdfdc69..ee3bc05f0e 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -233,7 +233,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUIDemo", "samples\R
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GpuInterop", "samples\GpuInterop\GpuInterop.csproj", "{C810060E-3809-4B74-A125-F11533AF9C1B}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\PublicAnalyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Analyzers", "src\tools\Avalonia.Analyzers\Avalonia.Analyzers.csproj", "{C692FE73-43DB-49CE-87FC-F03ED61F25C9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{176582E8-46AF-416A-85C1-13A5C6744497}"
ProjectSection(SolutionItems) = preProject
diff --git a/build/DevAnalyzers.props b/build/DevAnalyzers.props
index 7d021d051f..dffd3098c3 100644
--- a/build/DevAnalyzers.props
+++ b/build/DevAnalyzers.props
@@ -5,7 +5,7 @@
ReferenceOutputAssembly="false"
OutputItemType="Analyzer"
SetTargetFramework="TargetFramework=netstandard2.0"/>
-
+
diff --git a/samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs b/samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
index 549cf3d740..782435ae06 100644
--- a/samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
+++ b/samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
@@ -19,18 +19,16 @@ namespace ControlCatalog.Pages
public static readonly StyledProperty ScaleProperty = AvaloniaProperty.Register(nameof(Scale), 1.0d);
public double Scale { get => GetValue(ScaleProperty); set => SetValue(ScaleProperty, value); }
- public static readonly StyledProperty RotationProperty = AvaloniaProperty.Register(nameof(Rotation));
+ public static readonly StyledProperty RotationProperty = AvaloniaProperty.Register(nameof(Rotation),
+ coerce: (_, val) => val % (Math.PI * 2));
+
///
/// Rotation, measured in Radians!
///
public double Rotation
{
get => GetValue(RotationProperty);
- set
- {
- double valueToUse = value % (Math.PI * 2);
- SetValue(RotationProperty, valueToUse);
- }
+ set => SetValue(RotationProperty, value);
}
public static readonly StyledProperty ViewportCenterYProperty = AvaloniaProperty.Register(nameof(ViewportCenterY), 0.0d);
@@ -213,5 +211,6 @@ namespace ControlCatalog.Pages
return workingPoint;
}
+
}
}
diff --git a/samples/IntegrationTestApp/IntegrationTestApp.csproj b/samples/IntegrationTestApp/IntegrationTestApp.csproj
index 1356eeb526..398743a353 100644
--- a/samples/IntegrationTestApp/IntegrationTestApp.csproj
+++ b/samples/IntegrationTestApp/IntegrationTestApp.csproj
@@ -3,6 +3,7 @@
WinExe
net7.0
enable
+ $(NoWarn);AVP1012
diff --git a/src/Avalonia.Base/ClassBindingManager.cs b/src/Avalonia.Base/ClassBindingManager.cs
index a9726cb86e..55f3a7892a 100644
--- a/src/Avalonia.Base/ClassBindingManager.cs
+++ b/src/Avalonia.Base/ClassBindingManager.cs
@@ -17,6 +17,8 @@ namespace Avalonia
return target.Bind(prop, source, anchor);
}
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1001:The same AvaloniaProperty should not be registered twice",
+ Justification = "Classes.attr binding feature is implemented using intermediate avalonia properties for each class")]
private static AvaloniaProperty RegisterClassProxyProperty(string className)
{
var prop = AvaloniaProperty.Register("__AvaloniaReserved::Classes::" + className);
diff --git a/src/Avalonia.Base/Media/DashStyle.cs b/src/Avalonia.Base/Media/DashStyle.cs
index 4749bfa401..2529b9317d 100644
--- a/src/Avalonia.Base/Media/DashStyle.cs
+++ b/src/Avalonia.Base/Media/DashStyle.cs
@@ -44,6 +44,8 @@ namespace Avalonia.Media
///
/// The dashes collection.
/// The dash sequence offset.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
+ Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
public DashStyle(IEnumerable? dashes, double offset)
{
Dashes = (dashes as AvaloniaList) ?? new AvaloniaList(dashes ?? Array.Empty());
diff --git a/src/Avalonia.Base/Media/GradientBrush.cs b/src/Avalonia.Base/Media/GradientBrush.cs
index e1654a01b2..971d4fdd58 100644
--- a/src/Avalonia.Base/Media/GradientBrush.cs
+++ b/src/Avalonia.Base/Media/GradientBrush.cs
@@ -38,6 +38,8 @@ namespace Avalonia.Media
///
/// Initializes a new instance of the class.
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
+ Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
public GradientBrush()
{
this.GradientStops = new GradientStops();
diff --git a/src/Avalonia.Base/Media/PolyLineSegment.cs b/src/Avalonia.Base/Media/PolyLineSegment.cs
index d17a621348..51bf13d7cb 100644
--- a/src/Avalonia.Base/Media/PolyLineSegment.cs
+++ b/src/Avalonia.Base/Media/PolyLineSegment.cs
@@ -28,6 +28,8 @@ namespace Avalonia.Media
///
/// Initializes a new instance of the class.
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
+ Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
public PolyLineSegment()
{
Points = new Points();
diff --git a/src/Avalonia.Base/Media/TransformGroup.cs b/src/Avalonia.Base/Media/TransformGroup.cs
index 0465efd5a5..ae5e54c414 100644
--- a/src/Avalonia.Base/Media/TransformGroup.cs
+++ b/src/Avalonia.Base/Media/TransformGroup.cs
@@ -11,6 +11,8 @@ namespace Avalonia.Media
public static readonly StyledProperty ChildrenProperty =
AvaloniaProperty.Register(nameof(Children));
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
+ Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
public TransformGroup()
{
Children = new Transforms();
diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs
index 5881efce1e..3270fe4614 100644
--- a/src/Avalonia.Base/StyledElement.cs
+++ b/src/Avalonia.Base/StyledElement.cs
@@ -289,6 +289,7 @@ namespace Avalonia
public StyledElement? Parent { get; private set; }
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030:StyledProperty accessors should not have side effects", Justification = "False positive?")]
public ThemeVariant ActualThemeVariant => GetValue(ThemeVariant.ActualThemeVariantProperty);
///
diff --git a/src/Avalonia.Base/Styling/ThemeVariant.cs b/src/Avalonia.Base/Styling/ThemeVariant.cs
index 389136b0f5..23bc15dfa7 100644
--- a/src/Avalonia.Base/Styling/ThemeVariant.cs
+++ b/src/Avalonia.Base/Styling/ThemeVariant.cs
@@ -9,6 +9,10 @@ namespace Avalonia.Styling;
/// Specifies a UI theme variant that should be used for the Control and Application types.
///
[TypeConverter(typeof(ThemeVariantTypeConverter))]
+[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1010:AvaloniaProperty objects should be owned by the type in which they are stored",
+ Justification = "ActualThemeVariant and RequestedThemeVariant properties are shared Avalonia.Base and Avalonia.Controls projects," +
+ "but shouldn't be visible on the StyledElement class." +
+ "Ideally we woould introduce readonly styled properties.")]
public sealed record ThemeVariant
{
///
diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs
index 79cc760fc6..8717b5340a 100644
--- a/src/Avalonia.Base/Visual.cs
+++ b/src/Avalonia.Base/Visual.cs
@@ -329,6 +329,7 @@ namespace Avalonia
///
/// Gets the control's parent visual.
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "GetVisualParent extension method is supposed to be used instead.")]
internal Visual? VisualParent => _visualParent;
///
diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs
index be3d5424fb..e907fd5988 100644
--- a/src/Avalonia.Controls/Application.cs
+++ b/src/Avalonia.Controls/Application.cs
@@ -94,6 +94,8 @@ namespace Avalonia
}
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1031", Justification = "This property is supposed to be a styled readonly property.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030", Justification = "False positive.")]
public ThemeVariant ActualThemeVariant => GetValue(ActualThemeVariantProperty);
///
diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
index e10cc1d100..20711eecbc 100644
--- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
@@ -2042,6 +2042,8 @@ namespace Avalonia.Controls
///
/// Identifies the Value dependency property.
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1002:AvaloniaProperty objects should not be owned by a generic type",
+ Justification = "This property is not supposed to be used from XAML.")]
public static readonly StyledProperty ValueProperty =
AvaloniaProperty.Register, T>(nameof(Value));
diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs
index c17f5a19ab..fa956a79fe 100644
--- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteFilterMode.cs
@@ -9,7 +9,7 @@ namespace Avalonia.Controls
{
///
/// Specifies how text in the text box portion of the
- /// control is used to filter items specified by the
+ /// control is used to filter items specified by the
/// property for display in the drop-down.
///
public enum AutoCompleteFilterMode
diff --git a/src/Avalonia.Controls/ColumnDefinition.cs b/src/Avalonia.Controls/ColumnDefinition.cs
index 2eb3ae3010..b28faba863 100644
--- a/src/Avalonia.Controls/ColumnDefinition.cs
+++ b/src/Avalonia.Controls/ColumnDefinition.cs
@@ -46,8 +46,8 @@ namespace Avalonia.Controls
/// The width of the column.
/// The width unit of the column.
public ColumnDefinition(double value, GridUnitType type)
+ : this(new GridLength(value, type))
{
- Width = new GridLength(value, type);
}
///
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index fc4b11bd4a..63e28ea14d 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -56,6 +56,8 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1013",
+ Justification = "We keep PlacementModeProperty for backward compatibility.")]
public static readonly StyledProperty PlacementProperty =
Popup.PlacementProperty.AddOwner();
diff --git a/src/Avalonia.Controls/DefinitionBase.cs b/src/Avalonia.Controls/DefinitionBase.cs
index eb587fb157..d0752b8aa6 100644
--- a/src/Avalonia.Controls/DefinitionBase.cs
+++ b/src/Avalonia.Controls/DefinitionBase.cs
@@ -37,7 +37,7 @@ namespace Avalonia.Controls
{
// start with getting SharedSizeGroup value.
// this property is NOT inherited which should result in better overall perf.
- if (SharedSizeGroup is { } sharedSizeGroupId && PrivateSharedSizeScope is { } privateSharedSizeScope)
+ if (SharedSizeGroup is { } sharedSizeGroupId && GetValue(PrivateSharedSizeScopeProperty) is { } privateSharedSizeScope)
{
_sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
_sharedState.AddMember(this);
@@ -333,7 +333,7 @@ namespace Avalonia.Controls
if (definition._sharedState == null
&& sharedSizeGroupId != null
- && definition.PrivateSharedSizeScope is { } privateSharedSizeScope)
+ && definition.GetValue(PrivateSharedSizeScopeProperty) is { } privateSharedSizeScope)
{
// if definition is not registered and both: shared size group id AND private shared scope
// are available, then register definition.
@@ -412,14 +412,6 @@ namespace Avalonia.Controls
}
}
- ///
- /// Private getter of shared state collection dynamic property.
- ///
- private SharedSizeScope? PrivateSharedSizeScope
- {
- get { return GetValue(PrivateSharedSizeScopeProperty); }
- }
-
///
/// Convenience accessor to UseSharedMinimum flag
///
diff --git a/src/Avalonia.Controls/Documents/Span.cs b/src/Avalonia.Controls/Documents/Span.cs
index d3565cbdd5..7931ecbbde 100644
--- a/src/Avalonia.Controls/Documents/Span.cs
+++ b/src/Avalonia.Controls/Documents/Span.cs
@@ -18,6 +18,8 @@ namespace Avalonia.Controls.Documents
AvaloniaProperty.Register(
nameof(Inlines));
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
+ Justification = "Collection properties shouldn't be set with SetCurrentValue.")]
public Span()
{
Inlines = new InlineCollection
diff --git a/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs b/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs
index 9ed4737c7c..5b23b5030f 100644
--- a/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs
+++ b/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs
@@ -44,7 +44,7 @@ namespace Avalonia.Controls.Primitives
/// Defines the property
///
public static readonly StyledProperty OverlayInputPassThroughElementProperty =
- Popup.OverlayInputPassThroughElementProperty.AddOwner();
+ Popup.OverlayInputPassThroughElementProperty.AddOwner();
private readonly Lazy _popupLazy;
private Rect? _enlargedPopupRect;
diff --git a/src/Avalonia.Controls/LayoutTransformControl.cs b/src/Avalonia.Controls/LayoutTransformControl.cs
index f747e278f0..06069a897e 100644
--- a/src/Avalonia.Controls/LayoutTransformControl.cs
+++ b/src/Avalonia.Controls/LayoutTransformControl.cs
@@ -63,7 +63,7 @@ namespace Avalonia.Controls
{
if (TransformRoot == null || LayoutTransform == null)
{
- LayoutTransform = RenderTransform;
+ SetCurrentValue(LayoutTransformProperty, RenderTransform);
return base.ArrangeOverride(finalSize);
}
@@ -176,7 +176,7 @@ namespace Avalonia.Controls
else
{
_renderTransformChangedEvent?.Dispose();
- LayoutTransform = null;
+ ClearValue(LayoutTransformProperty);
}
}
}
diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs
index e665c2db90..e5f0b50555 100644
--- a/src/Avalonia.Controls/ListBox.cs
+++ b/src/Avalonia.Controls/ListBox.cs
@@ -29,18 +29,24 @@ namespace Avalonia.Controls
///
/// Defines the property.
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1010",
+ Justification = "This property is owned by SelectingItemsControl, but protected there. ListBox changes its visibility.")]
public static readonly new DirectProperty SelectedItemsProperty =
SelectingItemsControl.SelectedItemsProperty;
///
/// Defines the property.
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1010",
+ Justification = "This property is owned by SelectingItemsControl, but protected there. ListBox changes its visibility.")]
public static readonly new DirectProperty SelectionProperty =
SelectingItemsControl.SelectionProperty;
///
/// Defines the property.
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1010",
+ Justification = "This property is owned by SelectingItemsControl, but protected there. ListBox changes its visibility.")]
public static readonly new StyledProperty SelectionModeProperty =
SelectingItemsControl.SelectionModeProperty;
@@ -84,6 +90,8 @@ namespace Avalonia.Controls
/// Note that the selection mode only applies to selections made via user interaction.
/// Multiple selections can be made programmatically regardless of the value of this property.
///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1012",
+ Justification = "This property is owned by SelectingItemsControl, but protected there. ListBox changes its visibility.")]
public new SelectionMode SelectionMode
{
get { return base.SelectionMode; }
diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs
index a0dbf33a1d..72febcfedb 100644
--- a/src/Avalonia.Controls/MenuItem.cs
+++ b/src/Avalonia.Controls/MenuItem.cs
@@ -53,12 +53,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty InputGestureProperty =
AvaloniaProperty.Register