Add BindablePropertyDefaultValueCreatorAnalyzer to to avoid Static References from being passed into CreateDefaultValueMethodName when using [BindableProperty] and [AttachedBindableProperty<T>].#3096
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request adds a new Roslyn analyzer (BindablePropertyDefaultValueCreatorAnalyzer, diagnostic ID MCT003) to detect and prevent the use of static readonly fields or properties as return values in DefaultValueCreatorMethodName methods for both BindableProperty and AttachedBindableProperty attributes. This continues the bug fix from PR #3095, which addressed an issue where shared static instances caused state to be inadvertently shared across multiple BindableObject instances.
Changes:
- Adds a new analyzer that detects static readonly references in DefaultValueCreator methods and emits a warning
- Promotes the MCT003 warning to an error in Directory.Build.props to enforce compliance
- Fixes one violation found in NavigationBar.CreateColorDefaultValue by replacing Colors.Transparent with Color.FromUint(0u)
- Includes comprehensive unit tests covering various scenarios including conditional expressions, switch expressions, and edge cases
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/CommunityToolkit.Maui.Analyzers/BindablePropertyDefaultValueCreatorAnalyzer.cs | New analyzer implementation that detects static readonly field/property returns in DefaultValueCreator methods with support for complex expressions |
| src/CommunityToolkit.Maui.Analyzers/Resources.resx | Added localized resource strings for the new analyzer diagnostic messages |
| src/CommunityToolkit.Maui.Analyzers/Resources.Designer.cs | Auto-generated resource accessor code with version update to 18.0.0.0 |
| src/CommunityToolkit.Maui.Analyzers.UnitTests/BindablePropertyDefaultValueCreatorAnalyzerTests.cs | Comprehensive test suite with 22 tests covering positive and negative cases for BindableProperty |
| src/CommunityToolkit.Maui.Analyzers.UnitTests/AttachedBindablePropertyDefaultValueCreatorAnalyzerTests.cs | Comprehensive test suite with 19 tests covering positive and negative cases for AttachedBindableProperty |
| src/CommunityToolkit.Maui/PlatformConfiguration/AndroidSpecific/NavigationBar.shared.cs | Fixed violation by changing from Colors.Transparent to Color.FromUint(0u) |
| src/CommunityToolkit.Maui/CommunityToolkit.Maui.csproj | Added project reference to the Analyzers project |
| Directory.Build.props | Added MCT003 to WarningsAsErrors to promote the warning to an error |
Files not reviewed (1)
- src/CommunityToolkit.Maui.Analyzers/Resources.Designer.cs: Language not supported
src/CommunityToolkit.Maui.Analyzers/BindablePropertyDefaultValueCreatorAnalyzer.cs
Outdated
Show resolved
Hide resolved
…ueCreatorAnalyzer.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
...ommunityToolkit.Maui.Analyzers.UnitTests/BindablePropertyDefaultValueCreatorAnalyzerTests.cs
Show resolved
Hide resolved
bijington
left a comment
There was a problem hiding this comment.
The changes look good. Just one question around whether we should be covering the const option
...Toolkit.Maui.Analyzers.UnitTests/AttachedBindablePropertyDefaultValueCreatorAnalyzerTests.cs
Show resolved
Hide resolved
...Toolkit.Maui.Analyzers.UnitTests/AttachedBindablePropertyDefaultValueCreatorAnalyzerTests.cs
Show resolved
Hide resolved
Thanks Shaun! Nah, we're good. The bug occurs when a static reference is used that is writable. Since we can't write to a const, it won't produce the bug. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 11 changed files in this pull request and generated 4 comments.
Files not reviewed (1)
- src/CommunityToolkit.Maui.Analyzers/Resources.Designer.cs: Language not supported
Comments suppressed due to low confidence (1)
src/CommunityToolkit.Maui.Analyzers/BindablePropertyDefaultValueCreatorAnalyzer.cs:332
- The diagnostic message/resources say "static readonly field or property", but the analyzer logic flags any static field/property (regardless of readonly). Either update the analyzer to check readonly as well, or adjust the rule title/message/description (and related helper method names) to match the intended behavior (e.g., warn on any static shared instance).
}
}
| static MethodDeclarationSyntax? FindMethodInType(TypeDeclarationSyntax typeDeclaration, string methodName) | ||
| { | ||
| return typeDeclaration | ||
| .DescendantNodes() | ||
| .OfType<MethodDeclarationSyntax>() | ||
| .FirstOrDefault(method => method.Identifier.ValueText == methodName); | ||
| } |
There was a problem hiding this comment.
FindMethodInType uses DescendantNodes(), which can pick up methods inside nested types and miss methods declared in other partial declarations of the same type (in different files). Consider resolving the containing type symbol via the semantic model and using INamedTypeSymbol.GetMembers(methodName) (or restricting to direct members) to avoid false positives/negatives.
| static bool DoesReturnStaticReadOnlyFieldOrProperty(MethodDeclarationSyntax methodDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken) | ||
| { | ||
| var returnStatements = methodDeclaration | ||
| .DescendantNodes() | ||
| .OfType<ReturnStatementSyntax>(); | ||
|
|
There was a problem hiding this comment.
DoesReturnStaticReadOnlyFieldOrProperty enumerates return statements via DescendantNodes(), which also walks into lambdas/local functions inside the method. A nested return inside a lambda can incorrectly trigger the diagnostic even if the default value creator itself returns a new instance. Consider limiting traversal to the method body excluding nested functions/lambdas.
...ommunityToolkit.Maui.Analyzers.UnitTests/BindablePropertyDefaultValueCreatorAnalyzerTests.cs
Outdated
Show resolved
Hide resolved
src/CommunityToolkit.Maui.Analyzers/BindablePropertyDefaultValueCreatorAnalyzer.cs
Show resolved
Hide resolved
src/CommunityToolkit.Maui.Analyzers/AnalyzerReleases.Shipped.md
Outdated
Show resolved
Hide resolved
src/CommunityToolkit.Maui/PlatformConfiguration/AndroidSpecific/NavigationBar.shared.cs
Show resolved
Hide resolved
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
Thanks @pictos + @bijington! Could we get one more review? I've updated the Analyzer to support both |
| return propertyDeclaration; | ||
| } | ||
|
|
||
| static bool DoesReturnStaticReadOnlyFieldOrProperty(MethodDeclarationSyntax methodDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken) |
There was a problem hiding this comment.
The method name "DoesReturnStaticReadOnlyFieldOrProperty" suggests it only checks for readonly static members, but the implementation at line 324 actually checks for ANY static field (non-const) or static property, regardless of whether it's readonly. Consider renaming to "DoesReturnStaticFieldOrProperty" to better reflect the actual behavior and avoid confusion.
| static bool DoesReturnStaticReadOnlyFieldOrProperty(MethodDeclarationSyntax methodDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken) | |
| static bool DoesReturnStaticFieldOrProperty(MethodDeclarationSyntax methodDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken) |
| return false; | ||
| } | ||
|
|
||
| static bool DoesDelegateInitializerReturnStaticReadOnlyMember(SyntaxNode memberDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken) |
There was a problem hiding this comment.
The method name "DoesDelegateInitializerReturnStaticReadOnlyMember" suggests it only checks for readonly static members, but the implementation delegates to "IsStaticFieldOrProperty" which checks for ANY static field (non-const) or static property. Consider renaming to "DoesDelegateInitializerReturnStaticMember" to better reflect the actual behavior.
BindablePropertyDefaultValueCreatorAnalyzer to avoid future implementationsBindablePropertyDefaultValueCreatorAnalyzer to to avoid Static References from being passed into CreateDefaultValueMethodName when using [BindableProperty] and [AttachedBindableProperty<T>].
Description of Change
This PR continues the effort started in #3095 to avoid Static References from being passed into
CreateDefaultValueMethodNamewhen using[BindableProperty]and[AttachedBindableProperty<T>].This PR adds the
BindablePropertyDefaultValueCreatorAnalyzerto detect these scenarios and generate a Warning when one is found. This Analyzer is public and will also be leveraged by any developer usingCommunityToolkit.Maui. It also adds a comprehensive unit test suite to verify the different scenarios where a static reference may be passed intoCreateDefaultValueMethodName.To ensure we on the Community Toolkit team do not introduce improper usage I have promoted this Warning to an Error in
Directory.Build.props:<WarningsAsErrors>MCT003</WarnindsAsErrors>.Lastly, this PR fixes the one discovered violation of this new Analyzer, found in
NavigationBar.CreateColorDefaultValue.PR Checklist
approved(bug) orChampioned(feature/proposal)mainat time of PRAdditional information
This PR also adds Benchmarks for the new Analyzer