├── .editorconfig ├── .gitattributes ├── .github └── dependabot.yml ├── .gitignore ├── LICENSE ├── PropertyChangedAnalyzers.Benchmarks ├── .editorconfig ├── AssemblyAttributes.cs ├── BenchmarkTests.cs ├── Benchmarks │ ├── AllBenchmarks.cs │ ├── AllBenchmarks.md │ ├── ArgumentAnalyzerBenchmarks.cs │ ├── ArgumentAnalyzerBenchmarks.md │ ├── AssignmentAnalyzerBenchmarks.cs │ ├── AssignmentAnalyzerBenchmarks.md │ ├── ClassDeclarationAnalyzerBenchmarks.cs │ ├── ClassDeclarationAnalyzerBenchmarks.md │ ├── Codegen │ │ ├── Code.cs │ │ └── CodeGen.cs │ ├── EventAnalyzerBenchmarks.cs │ ├── EventAnalyzerBenchmarks.md │ ├── InvocationAnalyzerBenchmarks.cs │ ├── InvocationAnalyzerBenchmarks.md │ ├── MethodDeclarationAnalyzerBenchmarks.cs │ ├── MethodDeclarationAnalyzerBenchmarks.md │ ├── MutationAnalyzerBenchmarks.cs │ ├── MutationAnalyzerBenchmarks.md │ ├── PropertyDeclarationAnalyzerBenchmarks.cs │ ├── PropertyDeclarationAnalyzerBenchmarks.md │ ├── SetAccessorAnalyzerBenchmarks.cs │ ├── SetAccessorAnalyzerBenchmarks.md │ ├── StructAnalyzerBenchmarks.cs │ └── StructAnalyzerBenchmarks.md ├── Program.cs ├── PropertyChangedAnalyzers.Benchmarks.csproj └── PropertyChangedAnalyzers.Benchmarks.csproj.DotSettings ├── PropertyChangedAnalyzers.Test ├── .editorconfig ├── AssemblyAttributes.cs ├── DocumentationTests.cs ├── HandlesRecursion.cs ├── Helpers │ ├── GetterTests.cs │ ├── LibrarySettings.cs │ ├── MutableAutoPropertyTests.cs │ ├── OnPropertyChangedTests.Find.cs │ ├── OnPropertyChangedTests.MatchMethodSymbol.cs │ ├── PropertyChangedEventArgsTests.cs │ ├── PropertyChangedTest.FindPropertyName.cs │ ├── PropertyChangedTest.InvokesPropertyChangedFor.cs │ ├── PropertyTests.ShouldNotify.cs │ ├── PropertyTests.cs │ ├── SetterTests.FindBackingField.cs │ ├── SyntaxTreeHelpers │ │ └── PropertyDeclarationSyntaxExtTests.cs │ ├── TrySetTests.Find.cs │ └── TrySetTests.IsMatchMethod.cs ├── INPC001ImplementINotifyPropertyChanged │ ├── CodeFix.CS0246.cs │ ├── CodeFix.CS0535.cs │ ├── CodeFix.CaliburnMicro.cs │ ├── CodeFix.MvvmCrossCore.cs │ ├── CodeFix.MvvmLight.cs │ ├── CodeFix.PrismBindableBase.cs │ ├── CodeFix.StyletMvvm.cs │ ├── CodeFix.cs │ ├── NoFix.cs │ ├── Valid.Ignores.cs │ ├── Valid.ThirdParty.cs │ └── Valid.cs ├── INPC002MutablePublicPropertyShouldNotify │ ├── CodeFix.AutoProperty.cs │ ├── CodeFix.CaliburnMicro.PropertyChangedBase.cs │ ├── CodeFix.CaliburnMicro.Screen.cs │ ├── CodeFix.Equality.cs │ ├── CodeFix.MvvmCrossCore.cs │ ├── CodeFix.MvvmLight.cs │ ├── CodeFix.PrismBindableBase.cs │ ├── CodeFix.Repros.cs │ ├── CodeFix.Style.cs │ ├── CodeFix.StyletMvvm.cs │ ├── CodeFix.UnknownBinary.cs │ ├── CodeFix.ViewModelBase.cs │ ├── CodeFix.ViewModelBaseSubclassingPropertyChangedBase.cs │ ├── CodeFix.WithBackingFieldNotify.cs │ ├── CodeFix.WithBackingFieldNotifyWhenValueChanges.cs │ ├── CodeFix.cs │ ├── FixAll.cs │ ├── NoFix.cs │ ├── Valid.CaliburnMicro.cs │ ├── Valid.Ignores.cs │ ├── Valid.MvvmCrossCore.cs │ ├── Valid.MvvmLight.cs │ ├── Valid.PrismBindableBase.cs │ ├── Valid.StyletMvvm.cs │ ├── Valid.ViewModelBase.cs │ └── Valid.cs ├── INPC003NotifyForDependentProperty │ ├── CodeFix.CaliburnMicro.cs │ ├── CodeFix.GenericViewModelBase.cs │ ├── CodeFix.MvvmCrossCore.cs │ ├── CodeFix.MvvmLight.cs │ ├── CodeFix.Order.cs │ ├── CodeFix.PrismBindableBase.cs │ ├── CodeFix.Repros.cs │ ├── CodeFix.StyletMvvm.cs │ ├── CodeFix.ViewModelBase.cs │ ├── CodeFix.ViewModelBaseNotInSource.cs │ ├── CodeFix.cs │ ├── FixAll.cs │ ├── Valid.CaliburnMicro.cs │ ├── Valid.GenericViewModelBase.cs │ ├── Valid.Ignore.cs │ ├── Valid.MvvmCrossCore.cs │ ├── Valid.MvvmLight.cs │ ├── Valid.PrismBindableBase.cs │ ├── Valid.StyletMvvm.cs │ ├── Valid.ViewModelBase.cs │ ├── Valid.ViewModelBaseNotInSource.cs │ └── Valid.cs ├── INPC004UseCallerMemberName │ ├── CodeFix.Argument.cs │ ├── CodeFix.Method.cs │ ├── FixAll.cs │ └── Valid{T}.cs ├── INPC005CheckIfDifferentBeforeNotifying │ ├── CodeFix.CaliburnMicro.cs │ ├── CodeFix.MvvmCrossCore.cs │ ├── CodeFix.MvvmLight.cs │ ├── CodeFix.PrismBindableBase.cs │ ├── CodeFix.StyletMvvm.cs │ ├── CodeFix.ViewModelBase.cs │ ├── CodeFix.WhenCheck.cs │ ├── CodeFix.WhenNoCheck.cs │ ├── CodeFix.cs │ ├── NoFix.cs │ ├── Valid.CaliburnMicro.cs │ ├── Valid.MvvmCrossCore.cs │ ├── Valid.MvvmLight.cs │ ├── Valid.PrismBindableBase.cs │ ├── Valid.StyletMvvm.cs │ ├── Valid.ViewModelBase.cs │ └── Valid.cs ├── INPC006UseObjectEqualsForReferenceTypes │ ├── CodeFix.cs │ └── Valid.cs ├── INPC006UseReferenceEquals │ ├── CodeFix.cs │ └── Valid.cs ├── INPC007MissingInvoker │ ├── CodeFix.cs │ └── Valid.cs ├── INPC008StructMustNotNotify │ ├── Diagnostics.cs │ └── Valid.cs ├── INPC009NotifiesForMissingProperty │ ├── Diagnostics.Argument.cs │ ├── Diagnostics.Invocation.cs │ └── Valid{T}.cs ├── INPC010GetAndSetSame │ ├── Diagnostics.cs │ └── Valid.cs ├── INPC011DoNotShadow │ ├── Codefix.cs │ └── Valid.cs ├── INPC012DoNotUseExpression │ ├── Codefix.CaliburnMicro.cs │ ├── Codefix.MvvmCrossCore.cs │ ├── Codefix.MvvmLight.cs │ ├── Codefix.StyletMvvm.cs │ ├── Codefix.ViewModelBase.cs │ ├── Codefix.cs │ └── Valid.cs ├── INPC013UseNameof │ ├── CodeFix.cs │ └── Valid.cs ├── INPC014PreferSettingBackingFieldInCtor │ ├── CodeFix.cs │ └── Valid.cs ├── INPC015PropertyIsRecursive │ ├── Diagnostics.cs │ └── Valid.cs ├── INPC016NotifyAfterUpdate │ ├── Diagnostics.cs │ └── Valid.cs ├── INPC017BackingFieldNameMustMatch │ ├── CodeFix.cs │ └── Valid.cs ├── INPC018InvokerShouldBeProtected │ ├── CodeFix.cs │ └── Valid.cs ├── INPC019GetBackingField │ ├── CodeFix.cs │ └── Valid.cs ├── INPC020PreferExpressionBodyAccessor │ ├── CodeFix.cs │ └── FixAll.cs ├── INPC021SetBackingField │ └── Diagnostics.cs ├── INPC022EqualToBackingField │ ├── CodeFix.cs │ └── Valid.cs ├── INPC023InstanceEquals │ ├── CodeFix.cs │ └── Valid.cs ├── INPC024ReferenceEqualsValueType │ └── CodeFix.cs ├── ModuleInitializer.cs ├── NullableFixTests │ └── NullableFixTests.cs ├── PropertyChangedAnalyzers.Test.csproj ├── PropertyChangedAnalyzers.Test.csproj.DotSettings ├── ReproBox.cs ├── TestHelpers │ └── Code.cs └── ValidWithAllAnalyzers.cs ├── PropertyChangedAnalyzers.Vsix ├── Properties │ └── launchSettings.json ├── PropertyChangedAnalyzers.Vsix.csproj └── source.extension.vsixmanifest ├── PropertyChangedAnalyzers.sln ├── PropertyChangedAnalyzers.sln.DotSettings ├── PropertyChangedAnalyzers.snk ├── PropertyChangedAnalyzers ├── .editorconfig ├── AnalyzerCategory.cs ├── Analyzers │ ├── ArgumentAnalyzer.cs │ ├── AssignmentAnalyzer.cs │ ├── ClassDeclarationAnalyzer.cs │ ├── EventAnalyzer.cs │ ├── InvocationAnalyzer.cs │ ├── MethodDeclarationAnalyzer.cs │ ├── MutationAnalyzer.cs │ ├── PropertyDeclarationAnalyzer.cs │ ├── SetAccessorAnalyzer.cs │ └── StructAnalyzer.cs ├── AssemblyAttributes.cs ├── CodeFixes │ ├── AddOnPropertyChangedFix.cs │ ├── CheckIfDifferentBeforeNotifyFix.cs │ ├── EqualityFix.cs │ ├── ExpressionBodyFix.cs │ ├── Helpers │ │ ├── DocumentEditorInpcExt.cs │ │ ├── InpcFactory.cs │ │ ├── MakePropertyNotifyHelper.cs │ │ └── Messages.cs │ ├── ImplementINotifyPropertyChangedFix.cs │ ├── MakePropertyNotifyFix.cs │ ├── MakeProtectedFix.cs │ ├── NotifyForDependentPropertyFix.cs │ ├── NullableFix.cs │ ├── RemoveExpressionFix.cs │ ├── RemoveShadowingFix.cs │ ├── RenameFix.cs │ ├── ReplaceExpressionFix.cs │ ├── SetBackingFieldFix.cs │ ├── UseCallerMemberNameFix.cs │ └── UseNameofFix.cs ├── Descriptors.cs ├── Helpers │ ├── AnalysisResult.cs │ ├── AnalysisResult{T}.cs │ ├── BackingMemberAndValue.cs │ ├── CallerMemberNameAttribute.cs │ ├── Comparers │ │ └── SyntaxTokenComparer.cs │ ├── Getter.cs │ ├── KnownSymbols │ │ ├── CaliburnMicroPropertyChangedBase.cs │ │ ├── DependencyObjectType.cs │ │ ├── EqualityComparerOfTType.cs │ │ ├── INotifyPropertyChangedType.cs │ │ ├── KnownSymbol.cs │ │ ├── MicrosoftPracticesPrismMvvmBindableBase.cs │ │ ├── MvvmCrossCoreMvxNotifyPropertyChanged.cs │ │ ├── MvvmCrossMvxNotifyPropertyChanged.cs │ │ ├── MvvmLightObservableObject.cs │ │ ├── MvvmLightViewModelBase.cs │ │ ├── NullableOfTType.cs │ │ ├── NullableType.cs │ │ ├── ObjectType.cs │ │ ├── PropertyChangedEventHandlerType.cs │ │ ├── StringType.cs │ │ └── StyletPropertyChangedBase.cs │ ├── MutableAutoProperty.cs │ ├── MutableProperty.cs │ ├── OnPropertyChanged.cs │ ├── Property.cs │ ├── PropertyChanged.cs │ ├── PropertyChangedEventArgs.cs │ ├── PropertyNameArgument.cs │ ├── PropertyPath.cs │ ├── Setter.cs │ ├── SyntaxtTreeHelpers │ │ ├── FieldDeclarationSyntaxExt.cs │ │ └── IfStatementSyntaxExt.cs │ ├── TrySet.cs │ ├── TrySetMatch.cs │ └── Walkers │ │ ├── AssignmentWalker.cs │ │ ├── IdentifierNameWalker.cs │ │ ├── IdentifierTypeWalker.cs │ │ ├── InvocationWalker.cs │ │ └── ReturnExpressionsWalker.cs ├── PropertyChangedAnalyzers.csproj ├── PropertyChangedAnalyzers.csproj.DotSettings └── tools │ ├── install.ps1 │ └── uninstall.ps1 ├── README.md ├── RELEASE_NOTES.md ├── ValidCode ├── .editorconfig ├── CachingInConcurrentDictionary.cs ├── CustomControl.cs ├── DisposableFoo.cs ├── DontRequireNotificationFor.cs ├── EmptyTrySet.cs ├── Ignores │ ├── Abstract.cs │ ├── Enumerator.cs │ ├── FooControl.cs │ ├── IgnoredProperties.cs │ ├── IntEnumerator.cs │ ├── SomeStream.cs │ └── Struct.cs ├── Inheritance │ ├── ExpressionBodies.cs │ ├── ExpressionBodiesViewModelBase.cs │ ├── StatementBodies.cs │ ├── StatementBodiesViewModelBase.cs │ ├── UnderscoreNames.cs │ └── UnderscoreNamesViewModelBase.cs ├── InheritanceTrySet │ ├── ExpressionBodies.cs │ ├── ExpressionBodiesViewModelBase.cs │ ├── StatementBodies.cs │ ├── StatementBodiesViewModelBase.cs │ ├── UnderscoreNames.cs │ └── UnderscoreNamesViewModelBase.cs ├── IntAndStringProperty.cs ├── Interfaces │ ├── Generic.cs │ ├── GenericAutoProperty.cs │ ├── GenericClass.cs │ ├── IValue.cs │ ├── WithString.cs │ └── WithStringAutoProperty.cs ├── LockInSetter.cs ├── Mouse.cs ├── NotMutations.cs ├── Recursion │ ├── ExpressionBodies.cs │ ├── NotRecursion.cs │ └── StatementBodies.cs ├── RelayProperty.cs ├── Repros │ └── Issue102.cs ├── StaticClass.cs ├── TrySet │ ├── ExpressionBodies.cs │ ├── StatementBodies.cs │ └── UnderscoreNames.cs ├── ValidCode.csproj ├── Vanilla │ ├── ExpressionBodies.cs │ ├── StatementBodies.cs │ └── UnderscoreNames.cs ├── WithEventDeclaration.cs ├── WithEventDeclarationNullableDisabled.cs ├── WithMultidimensional.cs ├── WithSpeeds.cs ├── WithValidation.cs └── Wrapping │ ├── FrameworkTypes.cs │ ├── WithFields.cs │ ├── WrappingFields.cs │ └── WrappingProperties.cs ├── appveyor.yml ├── azure-pipelines.yml └── documentation ├── INPC001.md ├── INPC002.md ├── INPC003.md ├── INPC004.md ├── INPC005.md ├── INPC006_a.md ├── INPC006_b.md ├── INPC007.md ├── INPC008.md ├── INPC009.md ├── INPC010.md ├── INPC011.md ├── INPC012.md ├── INPC013.md ├── INPC014.md ├── INPC015.md ├── INPC016.md ├── INPC017.md ├── INPC018.md ├── INPC019.md ├── INPC020.md ├── INPC021.md ├── INPC022.md ├── INPC023.md └── INPC024.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Source code files 2 | *.cs text 3 | *.vsixmanifest text 4 | *.config text 5 | *.resx text 6 | *.vstemplate text 7 | *.nuspec text 8 | *.md text 9 | *.txt text 10 | *.ps1 text 11 | LICENSE text 12 | 13 | # Projects and solutions 14 | *.sln text 15 | *.csproj text 16 | 17 | # Certainly binary files 18 | *.png binary 19 | *.ico binary 20 | *.snk binary 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build output 2 | bin/ 3 | obj/ 4 | 5 | # Artifacts of the IDE and build 6 | *.sln.ide/ 7 | .vs/ 8 | packages/ 9 | *.suo 10 | *.user 11 | TestResults/ 12 | OpenCover.Reports/ 13 | .nuget/NuGet.exe 14 | build/nuget/ 15 | *.log 16 | 17 | # Visual Studio performance tools 18 | *.psess 19 | *.vsp 20 | *.vspx 21 | 22 | #ignore thumbnails created by windows 23 | Thumbs.db 24 | #Ignore files build by Visual Studio 25 | *.obj 26 | *.exe 27 | *.pdb 28 | *.aps 29 | *.pch 30 | *.vspscc 31 | *_i.c 32 | *_p.c 33 | *.ncb 34 | *.tlb 35 | *.tlh 36 | *.bak 37 | *.cache 38 | *.ilk 39 | [Bb]in 40 | [Dd]ebug*/ 41 | *.lib 42 | *.sbr 43 | [Rr]elease*/ 44 | _ReSharper*/ 45 | [Tt]est[Rr]esult* 46 | 47 | *.sln.docstates 48 | *.vssscc 49 | $tf*/ 50 | *.dll 51 | packages/* 52 | publish/* 53 | PropertyChangedAnalyzers.Demo/* 54 | .vs/* 55 | /paket-files/* 56 | /PropertyChangedAnalyzers.Benchmarks/BenchmarkDotNet.Artifacts/* 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 .NET Analyzers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # Default severity for analyzer diagnostics with category 'StyleCop.CSharp.DocumentationRules' 4 | dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = none 5 | # SA0001: XML comment analysis is disabled due to project configuration 6 | dotnet_diagnostic.SA0001.severity = none 7 | 8 | # CA1303: Do not pass literals as localized parameters 9 | dotnet_diagnostic.CA1303.severity = none 10 | 11 | # CA1822: Mark members as static 12 | dotnet_diagnostic.CA1822.severity = none 13 | 14 | # CA1062: Validate arguments of public methods 15 | dotnet_diagnostic.CA1062.severity = none 16 | 17 | # CA1724: The type name Code conflicts in whole or in part with the namespace name 'BenchmarkDotNet.Code'. Change either name to eliminate the conflict. 18 | dotnet_diagnostic.CA1724.severity = none 19 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/AssemblyAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | [assembly: CLSCompliant(false)] 4 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/BenchmarkTests.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Benchmarks; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using Microsoft.CodeAnalysis.Diagnostics; 7 | using NUnit.Framework; 8 | using PropertyChangedAnalyzers.Benchmarks.Benchmarks; 9 | 10 | public class BenchmarkTests 11 | { 12 | private static IReadOnlyList AllAnalyzers { get; } = 13 | typeof(KnownSymbol) 14 | .Assembly 15 | .GetTypes() 16 | .Where(t => typeof(DiagnosticAnalyzer).IsAssignableFrom(t) && !t.IsAbstract) 17 | .Select(t => (DiagnosticAnalyzer)Activator.CreateInstance(t)) 18 | .ToArray(); 19 | 20 | private static IReadOnlyList AllBenchmarks { get; } = AllAnalyzers 21 | .Select(x => Gu.Roslyn.Asserts.Benchmark.Create(Code.ValidCodeProject, x)) 22 | .ToArray(); 23 | 24 | [OneTimeSetUp] 25 | public void OneTimeSetUp() 26 | { 27 | foreach (var benchmark in AllBenchmarks) 28 | { 29 | benchmark.Run(); 30 | } 31 | } 32 | 33 | [TestCaseSource(nameof(AllBenchmarks))] 34 | public void Run(Gu.Roslyn.Asserts.Benchmark benchmark) 35 | { 36 | benchmark.Run(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/AllBenchmarks.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 4 | Intel Xeon CPU E5-2637 v4 3.50GHz, 2 CPU, 16 logical and 8 physical cores 5 | .NET Core SDK=3.1.101 6 | [Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 7 | DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated | 12 | |---------------------------- |------------:|-----------:|----------:|------------:|------:|------:|------:|----------:| 13 | | ArgumentAnalyzer | 786.93 us | 80.096 us | 233.64 us | 755.30 us | - | - | - | 8512 B | 14 | | AssignmentAnalyzer | 65.59 us | 6.335 us | 17.87 us | 61.85 us | - | - | - | 440 B | 15 | | ClassDeclarationAnalyzer | 3,564.37 us | 264.862 us | 759.94 us | 3,381.50 us | - | - | - | 214544 B | 16 | | EventAnalyzer | 45.19 us | 5.190 us | 14.98 us | 43.65 us | - | - | - | 2024 B | 17 | | InvocationAnalyzer | 149.92 us | 13.192 us | 35.21 us | 142.00 us | - | - | - | 696 B | 18 | | MethodDeclarationAnalyzer | 198.53 us | 28.005 us | 78.99 us | 163.20 us | - | - | - | 1216 B | 19 | | MutationAnalyzer | 377.61 us | 33.536 us | 94.59 us | 347.40 us | - | - | - | 440 B | 20 | | PropertyDeclarationAnalyzer | 942.49 us | 69.684 us | 201.05 us | 909.50 us | - | - | - | 30784 B | 21 | | SetAccessorAnalyzer | 173.75 us | 16.660 us | 45.89 us | 154.75 us | - | - | - | 1416 B | 22 | | StructAnalyzer | 32.22 us | 8.016 us | 23.38 us | 18.00 us | - | - | - | 440 B | 23 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/ArgumentAnalyzerBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable RedundantNameQualifier 2 | namespace PropertyChangedAnalyzers.Benchmarks.Benchmarks; 3 | 4 | [BenchmarkDotNet.Attributes.MemoryDiagnoser] 5 | public class ArgumentAnalyzerBenchmarks 6 | { 7 | private static readonly Gu.Roslyn.Asserts.Benchmark Benchmark = Gu.Roslyn.Asserts.Benchmark.Create(Code.ValidCodeProject, new PropertyChangedAnalyzers.ArgumentAnalyzer()); 8 | 9 | [BenchmarkDotNet.Attributes.Benchmark] 10 | public void RunOnValidCodeProject() 11 | { 12 | Benchmark.Run(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/ArgumentAnalyzerBenchmarks.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 4 | Intel Xeon CPU E5-2637 v4 3.50GHz, 2 CPU, 16 logical and 8 physical cores 5 | .NET Core SDK=3.1.101 6 | [Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 7 | DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | 12 | |---------------------- |---------:|---------:|---------:|------:|------:|------:|----------:| 13 | | RunOnValidCodeProject | 588.3 us | 47.45 us | 136.9 us | - | - | - | 8.31 KB | 14 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/AssignmentAnalyzerBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable RedundantNameQualifier 2 | namespace PropertyChangedAnalyzers.Benchmarks.Benchmarks; 3 | 4 | [BenchmarkDotNet.Attributes.MemoryDiagnoser] 5 | public class AssignmentAnalyzerBenchmarks 6 | { 7 | private static readonly Gu.Roslyn.Asserts.Benchmark Benchmark = Gu.Roslyn.Asserts.Benchmark.Create(Code.ValidCodeProject, new PropertyChangedAnalyzers.AssignmentAnalyzer()); 8 | 9 | [BenchmarkDotNet.Attributes.Benchmark] 10 | public void RunOnValidCodeProject() 11 | { 12 | Benchmark.Run(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/AssignmentAnalyzerBenchmarks.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 4 | Intel Xeon CPU E5-2637 v4 3.50GHz, 2 CPU, 16 logical and 8 physical cores 5 | .NET Core SDK=3.1.101 6 | [Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 7 | DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated | 12 | |---------------------- |---------:|---------:|---------:|---------:|------:|------:|------:|----------:| 13 | | RunOnValidCodeProject | 71.43 us | 5.566 us | 15.42 us | 66.70 us | - | - | - | 648 B | 14 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/ClassDeclarationAnalyzerBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable RedundantNameQualifier 2 | namespace PropertyChangedAnalyzers.Benchmarks.Benchmarks; 3 | 4 | [BenchmarkDotNet.Attributes.MemoryDiagnoser] 5 | public class ClassDeclarationAnalyzerBenchmarks 6 | { 7 | private static readonly Gu.Roslyn.Asserts.Benchmark Benchmark = Gu.Roslyn.Asserts.Benchmark.Create(Code.ValidCodeProject, new PropertyChangedAnalyzers.ClassDeclarationAnalyzer()); 8 | 9 | [BenchmarkDotNet.Attributes.Benchmark] 10 | public void RunOnValidCodeProject() 11 | { 12 | Benchmark.Run(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/ClassDeclarationAnalyzerBenchmarks.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 4 | Intel Xeon CPU E5-2637 v4 3.50GHz, 2 CPU, 16 logical and 8 physical cores 5 | .NET Core SDK=3.1.101 6 | [Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 7 | DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | 12 | |---------------------- |---------:|----------:|----------:|------:|------:|------:|----------:| 13 | | RunOnValidCodeProject | 2.951 ms | 0.2273 ms | 0.6484 ms | - | - | - | 125.66 KB | 14 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/Codegen/Code.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Benchmarks.Benchmarks; 2 | 3 | using System.IO; 4 | using Gu.Roslyn.Asserts; 5 | 6 | using Microsoft.CodeAnalysis; 7 | 8 | public static class Code 9 | { 10 | public static string ProjectDirectory { get; } = ProjectFile.Find("PropertyChangedAnalyzers.Benchmarks.csproj").DirectoryName; 11 | 12 | public static string BenchmarksDirectory { get; } = Path.Combine(ProjectDirectory, "Benchmarks"); 13 | 14 | public static Solution ValidCodeProject { get; } = CodeFactory.CreateSolution(ProjectFile.Find("ValidCode.csproj")); 15 | } 16 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/EventAnalyzerBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable RedundantNameQualifier 2 | namespace PropertyChangedAnalyzers.Benchmarks.Benchmarks; 3 | 4 | [BenchmarkDotNet.Attributes.MemoryDiagnoser] 5 | public class EventAnalyzerBenchmarks 6 | { 7 | private static readonly Gu.Roslyn.Asserts.Benchmark Benchmark = Gu.Roslyn.Asserts.Benchmark.Create(Code.ValidCodeProject, new PropertyChangedAnalyzers.EventAnalyzer()); 8 | 9 | [BenchmarkDotNet.Attributes.Benchmark] 10 | public void RunOnValidCodeProject() 11 | { 12 | Benchmark.Run(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/EventAnalyzerBenchmarks.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 4 | Intel Xeon CPU E5-2637 v4 3.50GHz, 2 CPU, 16 logical and 8 physical cores 5 | .NET Core SDK=3.1.101 6 | [Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 7 | DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | 12 | |---------------------- |---------:|---------:|---------:|------:|------:|------:|----------:| 13 | | RunOnValidCodeProject | 38.46 us | 3.884 us | 11.02 us | - | - | - | 1.98 KB | 14 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/InvocationAnalyzerBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable RedundantNameQualifier 2 | namespace PropertyChangedAnalyzers.Benchmarks.Benchmarks; 3 | 4 | [BenchmarkDotNet.Attributes.MemoryDiagnoser] 5 | public class InvocationAnalyzerBenchmarks 6 | { 7 | private static readonly Gu.Roslyn.Asserts.Benchmark Benchmark = Gu.Roslyn.Asserts.Benchmark.Create(Code.ValidCodeProject, new PropertyChangedAnalyzers.InvocationAnalyzer()); 8 | 9 | [BenchmarkDotNet.Attributes.Benchmark] 10 | public void RunOnValidCodeProject() 11 | { 12 | Benchmark.Run(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/InvocationAnalyzerBenchmarks.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 4 | Intel Xeon CPU E5-2637 v4 3.50GHz, 2 CPU, 16 logical and 8 physical cores 5 | .NET Core SDK=3.1.101 6 | [Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 7 | DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | 12 | |---------------------- |---------:|---------:|---------:|------:|------:|------:|----------:| 13 | | RunOnValidCodeProject | 128.6 us | 13.92 us | 37.38 us | - | - | - | 696 B | 14 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/MethodDeclarationAnalyzerBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable RedundantNameQualifier 2 | namespace PropertyChangedAnalyzers.Benchmarks.Benchmarks; 3 | 4 | [BenchmarkDotNet.Attributes.MemoryDiagnoser] 5 | public class MethodDeclarationAnalyzerBenchmarks 6 | { 7 | private static readonly Gu.Roslyn.Asserts.Benchmark Benchmark = Gu.Roslyn.Asserts.Benchmark.Create(Code.ValidCodeProject, new PropertyChangedAnalyzers.MethodDeclarationAnalyzer()); 8 | 9 | [BenchmarkDotNet.Attributes.Benchmark] 10 | public void RunOnValidCodeProject() 11 | { 12 | Benchmark.Run(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/MethodDeclarationAnalyzerBenchmarks.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 4 | Intel Xeon CPU E5-2637 v4 3.50GHz, 2 CPU, 16 logical and 8 physical cores 5 | .NET Core SDK=3.1.101 6 | [Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 7 | DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated | 12 | |---------------------- |---------:|---------:|---------:|---------:|------:|------:|------:|----------:| 13 | | RunOnValidCodeProject | 174.6 us | 22.02 us | 61.37 us | 148.0 us | - | - | - | 1.19 KB | 14 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/MutationAnalyzerBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable RedundantNameQualifier 2 | namespace PropertyChangedAnalyzers.Benchmarks.Benchmarks; 3 | 4 | [BenchmarkDotNet.Attributes.MemoryDiagnoser] 5 | public class MutationAnalyzerBenchmarks 6 | { 7 | private static readonly Gu.Roslyn.Asserts.Benchmark Benchmark = Gu.Roslyn.Asserts.Benchmark.Create(Code.ValidCodeProject, new PropertyChangedAnalyzers.MutationAnalyzer()); 8 | 9 | [BenchmarkDotNet.Attributes.Benchmark] 10 | public void RunOnValidCodeProject() 11 | { 12 | Benchmark.Run(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/MutationAnalyzerBenchmarks.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 4 | Intel Xeon CPU E5-2637 v4 3.50GHz, 2 CPU, 16 logical and 8 physical cores 5 | .NET Core SDK=3.1.101 6 | [Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 7 | DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated | 12 | |---------------------- |---------:|---------:|---------:|---------:|------:|------:|------:|----------:| 13 | | RunOnValidCodeProject | 333.9 us | 30.67 us | 85.99 us | 304.3 us | - | - | - | 440 B | 14 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/PropertyDeclarationAnalyzerBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable RedundantNameQualifier 2 | namespace PropertyChangedAnalyzers.Benchmarks.Benchmarks; 3 | 4 | [BenchmarkDotNet.Attributes.MemoryDiagnoser] 5 | public class PropertyDeclarationAnalyzerBenchmarks 6 | { 7 | private static readonly Gu.Roslyn.Asserts.Benchmark Benchmark = Gu.Roslyn.Asserts.Benchmark.Create(Code.ValidCodeProject, new PropertyChangedAnalyzers.PropertyDeclarationAnalyzer()); 8 | 9 | [BenchmarkDotNet.Attributes.Benchmark] 10 | public void RunOnValidCodeProject() 11 | { 12 | Benchmark.Run(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/PropertyDeclarationAnalyzerBenchmarks.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 4 | Intel Xeon CPU E5-2637 v4 3.50GHz, 2 CPU, 16 logical and 8 physical cores 5 | .NET Core SDK=3.1.101 6 | [Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 7 | DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | 12 | |---------------------- |---------:|----------:|----------:|------:|------:|------:|----------:| 13 | | RunOnValidCodeProject | 1.102 ms | 0.0798 ms | 0.2239 ms | - | - | - | 95.1 KB | 14 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/SetAccessorAnalyzerBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable RedundantNameQualifier 2 | namespace PropertyChangedAnalyzers.Benchmarks.Benchmarks; 3 | 4 | [BenchmarkDotNet.Attributes.MemoryDiagnoser] 5 | public class SetAccessorAnalyzerBenchmarks 6 | { 7 | private static readonly Gu.Roslyn.Asserts.Benchmark Benchmark = Gu.Roslyn.Asserts.Benchmark.Create(Code.ValidCodeProject, new PropertyChangedAnalyzers.SetAccessorAnalyzer()); 8 | 9 | [BenchmarkDotNet.Attributes.Benchmark] 10 | public void RunOnValidCodeProject() 11 | { 12 | Benchmark.Run(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/SetAccessorAnalyzerBenchmarks.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 4 | Intel Xeon CPU E5-2637 v4 3.50GHz, 2 CPU, 16 logical and 8 physical cores 5 | .NET Core SDK=3.1.101 6 | [Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 7 | DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated | 12 | |---------------------- |---------:|---------:|---------:|---------:|------:|------:|------:|----------:| 13 | | RunOnValidCodeProject | 166.0 us | 16.94 us | 46.95 us | 149.3 us | - | - | - | 1.38 KB | 14 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/StructAnalyzerBenchmarks.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable RedundantNameQualifier 2 | namespace PropertyChangedAnalyzers.Benchmarks.Benchmarks; 3 | 4 | [BenchmarkDotNet.Attributes.MemoryDiagnoser] 5 | public class StructAnalyzerBenchmarks 6 | { 7 | private static readonly Gu.Roslyn.Asserts.Benchmark Benchmark = Gu.Roslyn.Asserts.Benchmark.Create(Code.ValidCodeProject, new PropertyChangedAnalyzers.StructAnalyzer()); 8 | 9 | [BenchmarkDotNet.Attributes.Benchmark] 10 | public void RunOnValidCodeProject() 11 | { 12 | Benchmark.Run(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/Benchmarks/StructAnalyzerBenchmarks.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362 4 | Intel Xeon CPU E5-2637 v4 3.50GHz, 2 CPU, 16 logical and 8 physical cores 5 | .NET Core SDK=3.1.101 6 | [Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 7 | DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | 12 | |---------------------- |---------:|---------:|---------:|------:|------:|------:|----------:| 13 | | RunOnValidCodeProject | 24.96 us | 5.273 us | 15.21 us | - | - | - | 440 B | 14 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/PropertyChangedAnalyzers.Benchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Exe 6 | 7 | 8 | 9 | True 10 | AllEnabledByDefault 11 | latest 12 | 13 | 14 | 15 | true 16 | ../PropertyChangedAnalyzers.snk 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Benchmarks/PropertyChangedAnalyzers.Benchmarks.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # Default severity for analyzer diagnostics with category 'StyleCop.CSharp.DocumentationRules' 4 | dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = none 5 | # SA0001: XML comment analysis is disabled due to project configuration 6 | dotnet_diagnostic.SA0001.severity = none 7 | 8 | # CA1303: Do not pass literals as localized parameters 9 | dotnet_diagnostic.CA1303.severity = none 10 | 11 | # CA1030: Use events where appropriate 12 | dotnet_diagnostic.CA1030.severity = none 13 | 14 | # CA1034: Nested types should not be visible 15 | dotnet_diagnostic.CA1034.severity = none 16 | 17 | # CA1000: Do not declare static members on generic types 18 | dotnet_diagnostic.CA1000.severity = none 19 | 20 | # CA1724: The type name MvvmLight conflicts in whole or in part with the namespace name 'GalaSoft.MvvmLight'. Change either name to eliminate the conflict. 21 | dotnet_diagnostic.CA1724.severity = none 22 | 23 | # CA1062: Validate arguments of public methods 24 | dotnet_diagnostic.CA1062.severity = none 25 | 26 | # SA1203: Constants should appear before fields 27 | dotnet_diagnostic.SA1203.severity = none 28 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/AssemblyAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | [assembly: CLSCompliant(false)] 4 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/Helpers/GetterTests.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.Helpers; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using Microsoft.CodeAnalysis.CSharp; 5 | using NUnit.Framework; 6 | 7 | public static class GetterTests 8 | { 9 | [TestCase("P11", "this.p1")] 10 | [TestCase("P12", "this.p1")] 11 | [TestCase("P1", "this.p1")] 12 | [TestCase("P2", "this.p2")] 13 | public static void TrySingleReturned(string propertyName, string expected) 14 | { 15 | var syntaxTree = CSharpSyntaxTree.ParseText( 16 | """ 17 | namespace N 18 | { 19 | public class C 20 | { 21 | private int p1; 22 | private int p2; 23 | 24 | public int P11 => this.p1; 25 | 26 | public int P12 27 | { 28 | get => this.p1; 29 | } 30 | 31 | public int P1 32 | { 33 | get { return this.p1; } 34 | set { this.p1 = value; } 35 | } 36 | 37 | public int P2 38 | { 39 | get => this.p2; 40 | set => this.p2 = value; 41 | } 42 | } 43 | } 44 | """); 45 | var declaration = syntaxTree.FindPropertyDeclaration(propertyName); 46 | Assert.AreEqual(expected, Property.FindSingleReturned(declaration).ToString()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/Helpers/MutableAutoPropertyTests.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.Helpers; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using Microsoft.CodeAnalysis.CSharp; 5 | using NUnit.Framework; 6 | 7 | public static class MutableAutoPropertyTests 8 | { 9 | [TestCase("P1", false)] 10 | [TestCase("P2", true)] 11 | [TestCase("P3", false)] 12 | [TestCase("P4", false)] 13 | [TestCase("P5", false)] 14 | [TestCase("P6", false)] 15 | public static void Match(string propertyName, bool expected) 16 | { 17 | var syntaxTree = CSharpSyntaxTree.ParseText(""" 18 | namespace N 19 | { 20 | using System; 21 | using System.ComponentModel; 22 | using System.Runtime.CompilerServices; 23 | 24 | public class C 25 | { 26 | private readonly int p3; 27 | private readonly int p4; 28 | private int p5; 29 | private int p6; 30 | 31 | public int P1 { get; } 32 | 33 | public int P2 { get; set; } 34 | 35 | public int P3 => this.p3; 36 | 37 | public int P4 38 | { 39 | get 40 | { 41 | return this.p4; 42 | } 43 | } 44 | 45 | public int P5 46 | { 47 | get { return this.p5; } 48 | set { this.p5 = value; } 49 | } 50 | 51 | public int P6 52 | { 53 | get => this.p6; 54 | private set => this.p6 = value; 55 | } 56 | } 57 | } 58 | """); 59 | var property = syntaxTree.FindPropertyDeclaration(propertyName); 60 | Assert.AreEqual(expected, MutableAutoProperty.Match(property) is { }); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/Helpers/SyntaxTreeHelpers/PropertyDeclarationSyntaxExtTests.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.Helpers.SyntaxTreeHelpers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | using Gu.Roslyn.Asserts; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | using NUnit.Framework; 7 | 8 | public class PropertyDeclarationSyntaxExtTests 9 | { 10 | [TestCase("P1", "get { return this.p1; }")] 11 | [TestCase("P2", "private get { return value2; }")] 12 | public void TryGetGetAccessorDeclarationBlock(string propertyName, string getter) 13 | { 14 | var syntaxTree = CSharpSyntaxTree.ParseText(@" 15 | namespace N 16 | { 17 | public class C 18 | { 19 | private int p1; 20 | private int p2; 21 | 22 | public int P1 23 | { 24 | get { return this.p1; } 25 | set { this.p1 = value; } 26 | } 27 | 28 | public int P2 29 | { 30 | private get { return value2; } 31 | set { value2 = value; } 32 | } 33 | } 34 | }"); 35 | var property = syntaxTree.FindPropertyDeclaration(propertyName); 36 | Assert.AreEqual(true, property.TryGetGetter(out var result)); 37 | Assert.AreEqual(getter, result.ToString()); 38 | } 39 | 40 | [TestCase("P1", "set { this.p1 = value; }")] 41 | [TestCase("P2", "private set { value2 = value; }")] 42 | public void TryGetSetAccessorDeclarationBlock(string propertyName, string setter) 43 | { 44 | var syntaxTree = CSharpSyntaxTree.ParseText(@" 45 | namespace N 46 | { 47 | public class C 48 | { 49 | private int p1; 50 | private int p2; 51 | 52 | public int P1 53 | { 54 | get { return this.p1; } 55 | set { this.p1 = value; } 56 | } 57 | 58 | public int P2 59 | { 60 | get { return value2; } 61 | private set { value2 = value; } 62 | } 63 | } 64 | }"); 65 | var property = syntaxTree.FindPropertyDeclaration(propertyName); 66 | Assert.AreEqual(true, property.TryGetSetter(out var result)); 67 | Assert.AreEqual(setter, result.ToString()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC001ImplementINotifyPropertyChanged/NoFix.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC001ImplementINotifyPropertyChanged; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | 6 | public static class NoFix 7 | { 8 | private static readonly ClassDeclarationAnalyzer Analyzer = new(); 9 | private static readonly ImplementINotifyPropertyChangedFix Fix = new(); 10 | private static readonly ExpectedDiagnostic ExpectedDiagnostic = ExpectedDiagnostic.Create(Descriptors.INPC001ImplementINotifyPropertyChanged); 11 | 12 | [Test] 13 | [Ignore("Not sure how we want this.")] 14 | public static void IgnoresWhenBaseIsMouseGesture() 15 | { 16 | var code = @" 17 | namespace N 18 | { 19 | using System.Windows.Input; 20 | 21 | public class CustomGesture : MouseGesture 22 | { 23 | ↓public int P { get; set; } 24 | } 25 | }"; 26 | 27 | RoslynAssert.NoFix(Analyzer, Fix, ExpectedDiagnostic, code); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC001ImplementINotifyPropertyChanged/Valid.ThirdParty.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC001ImplementINotifyPropertyChanged; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | using PropertyChangedAnalyzers.Test.Helpers; 6 | 7 | public static partial class Valid 8 | { 9 | public static class ThirdParty 10 | { 11 | [Test] 12 | public static void MvvmLight() 13 | { 14 | var code = @" 15 | namespace N 16 | { 17 | public class C : GalaSoft.MvvmLight.ViewModelBase 18 | { 19 | public int P { get; set; } 20 | } 21 | }"; 22 | 23 | RoslynAssert.Valid(Analyzer, code, settings: LibrarySettings.MvvmLight); 24 | } 25 | 26 | [Test] 27 | public static void CaliburnMicro() 28 | { 29 | var code = @" 30 | namespace N 31 | { 32 | public class C : Caliburn.Micro.PropertyChangedBase 33 | { 34 | public int P { get; set; } 35 | } 36 | }"; 37 | 38 | RoslynAssert.Valid(Analyzer, code, settings: LibrarySettings.CaliburnMicro); 39 | } 40 | 41 | [Test] 42 | public static void Stylet() 43 | { 44 | var code = @" 45 | namespace N 46 | { 47 | public class C : Stylet.PropertyChangedBase 48 | { 49 | public int P { get; set; } 50 | } 51 | }"; 52 | 53 | RoslynAssert.Valid(Analyzer, code, settings: LibrarySettings.Stylet); 54 | } 55 | 56 | [Test] 57 | public static void MvvmCross() 58 | { 59 | var code = @" 60 | namespace N 61 | { 62 | public class C : MvvmCross.ViewModels.MvxNotifyPropertyChanged 63 | { 64 | public int P { get; set; } 65 | } 66 | }"; 67 | 68 | RoslynAssert.Valid(Analyzer, code, settings: LibrarySettings.MvvmCross); 69 | } 70 | 71 | [Test] 72 | public static void SubclassBindableBase() 73 | { 74 | var code = @" 75 | namespace N 76 | { 77 | public class C : Microsoft.Practices.Prism.Mvvm.BindableBase 78 | { 79 | public int P { get; set; } 80 | } 81 | }"; 82 | RoslynAssert.Valid(Analyzer, code, settings: LibrarySettings.Prism); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC002MutablePublicPropertyShouldNotify/NoFix.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC002MutablePublicPropertyShouldNotify; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | 6 | public static class NoFix 7 | { 8 | private static readonly SetAccessorAnalyzer Analyzer = new(); 9 | private static readonly MakePropertyNotifyFix Fix = new(); 10 | private static readonly ExpectedDiagnostic ExpectedDiagnostic = ExpectedDiagnostic.Create(Descriptors.INPC002MutablePublicPropertyShouldNotify); 11 | 12 | [Test] 13 | [Ignore("Not sure how we want this.")] 14 | public static void NoFixWhenBaseHasInternalOnPropertyChanged() 15 | { 16 | var code = @" 17 | namespace N 18 | { 19 | using System.Windows.Input; 20 | 21 | public class CustomGesture : MouseGesture 22 | { 23 | public int ↓P { get; set; } 24 | } 25 | }"; 26 | 27 | RoslynAssert.NoFix(Analyzer, Fix, ExpectedDiagnostic, code); 28 | } 29 | 30 | [Test] 31 | public static void AutoPropertyExplicitNameHandlesRecursionInInvoker() 32 | { 33 | var code = @" 34 | #pragma warning disable CS0067 35 | namespace N 36 | { 37 | using System.ComponentModel; 38 | 39 | public class C : INotifyPropertyChanged 40 | { 41 | public event PropertyChangedEventHandler? PropertyChanged; 42 | 43 | public int ↓P { get; set; } 44 | 45 | protected virtual void OnPropertyChanged(string propertyName) 46 | { 47 | this.OnPropertyChanged(propertyName); 48 | } 49 | } 50 | }"; 51 | 52 | RoslynAssert.NoFix(Analyzer, Fix, ExpectedDiagnostic, code); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC002MutablePublicPropertyShouldNotify/Valid.CaliburnMicro.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC002MutablePublicPropertyShouldNotify; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | using PropertyChangedAnalyzers.Test.Helpers; 6 | 7 | public static partial class Valid 8 | { 9 | public static class CaliburnMicro 10 | { 11 | private static readonly Settings MetadataReferences = LibrarySettings.CaliburnMicro; 12 | 13 | [Test] 14 | public static void Set() 15 | { 16 | var code = @" 17 | namespace N 18 | { 19 | public class C : Caliburn.Micro.PropertyChangedBase 20 | { 21 | private int p; 22 | 23 | public int P 24 | { 25 | get { return p; } 26 | set { this.Set(ref this.p, value); } 27 | } 28 | } 29 | }"; 30 | 31 | RoslynAssert.Valid(Analyzer, Descriptor, new[] { code }, settings: MetadataReferences); 32 | } 33 | 34 | [Test] 35 | public static void SetExpressionBodies() 36 | { 37 | var code = @" 38 | namespace N 39 | { 40 | public class C : Caliburn.Micro.PropertyChangedBase 41 | { 42 | private int p; 43 | 44 | public int P 45 | { 46 | get => p; 47 | set => this.Set(ref this.p, value); 48 | } 49 | } 50 | }"; 51 | 52 | RoslynAssert.Valid(Analyzer, code, settings: MetadataReferences); 53 | } 54 | 55 | [TestCase("null")] 56 | [TestCase("string.Empty")] 57 | [TestCase(@"""P""")] 58 | [TestCase(@"nameof(P)")] 59 | [TestCase(@"nameof(this.P)")] 60 | public static void RaisePropertyChanged(string propertyName) 61 | { 62 | var code = @" 63 | namespace N 64 | { 65 | public class C : Caliburn.Micro.PropertyChangedBase 66 | { 67 | private int p; 68 | 69 | public int P 70 | { 71 | get { return this.p; } 72 | set 73 | { 74 | if (value == this.p) return; 75 | this.p = value; 76 | this.NotifyOfPropertyChange(nameof(P)); 77 | } 78 | } 79 | } 80 | }".AssertReplace(@"nameof(P)", propertyName); 81 | 82 | RoslynAssert.Valid(Analyzer, Descriptor, new[] { code }, settings: MetadataReferences); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC002MutablePublicPropertyShouldNotify/Valid.MvvmCrossCore.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC002MutablePublicPropertyShouldNotify; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | using PropertyChangedAnalyzers.Test.Helpers; 6 | 7 | public static partial class Valid 8 | { 9 | public static class MvvmCrossCore 10 | { 11 | private static readonly Settings Settings = LibrarySettings.MvvmCross; 12 | 13 | [Test] 14 | public static void SetProperty() 15 | { 16 | var code = @" 17 | namespace N 18 | { 19 | public class C : MvvmCross.ViewModels.MvxNotifyPropertyChanged 20 | { 21 | private int p; 22 | 23 | public int P 24 | { 25 | get { return p; } 26 | set { this.SetProperty(ref this.p, value); } 27 | } 28 | } 29 | }"; 30 | 31 | RoslynAssert.Valid(Analyzer, Descriptor, new[] { code }, settings: Settings); 32 | } 33 | 34 | [Test] 35 | public static void SetExpressionBodies() 36 | { 37 | var code = @" 38 | namespace N 39 | { 40 | public class C : MvvmCross.ViewModels.MvxNotifyPropertyChanged 41 | { 42 | private int p; 43 | 44 | public int P 45 | { 46 | get => p; 47 | set => this.SetProperty(ref this.p, value); 48 | } 49 | } 50 | }"; 51 | 52 | RoslynAssert.Valid(Analyzer, code, settings: Settings); 53 | } 54 | 55 | [TestCase("(string?)null")] 56 | [TestCase("string.Empty")] 57 | [TestCase(@"""P""")] 58 | [TestCase(@"nameof(P)")] 59 | [TestCase(@"nameof(this.P)")] 60 | public static void RaisePropertyChanged(string propertyName) 61 | { 62 | var code = @" 63 | namespace N 64 | { 65 | public class C : MvvmCross.ViewModels.MvxNotifyPropertyChanged 66 | { 67 | private int p; 68 | 69 | public int P 70 | { 71 | get { return this.p; } 72 | set 73 | { 74 | if (value == this.p) return; 75 | this.p = value; 76 | this.RaisePropertyChanged(nameof(P)); 77 | } 78 | } 79 | } 80 | }".AssertReplace(@"nameof(P)", propertyName); 81 | 82 | RoslynAssert.Valid(Analyzer, Descriptor, new[] { code }, settings: Settings); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC002MutablePublicPropertyShouldNotify/Valid.MvvmLight.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC002MutablePublicPropertyShouldNotify; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | using PropertyChangedAnalyzers.Test.Helpers; 6 | 7 | public static partial class Valid 8 | { 9 | public static class MvvmLight 10 | { 11 | private static readonly Settings MetadataReferences = LibrarySettings.MvvmLight; 12 | 13 | [Test] 14 | public static void Set() 15 | { 16 | var code = @" 17 | namespace N 18 | { 19 | public class C : GalaSoft.MvvmLight.ViewModelBase 20 | { 21 | private int p; 22 | 23 | public int P 24 | { 25 | get { return p; } 26 | set { this.Set(ref this.p, value); } 27 | } 28 | } 29 | }"; 30 | 31 | RoslynAssert.Valid(Analyzer, Descriptor, new[] { code }, settings: MetadataReferences); 32 | } 33 | 34 | [Test] 35 | public static void SetExpressionBodies() 36 | { 37 | var code = @" 38 | namespace N 39 | { 40 | public class C : GalaSoft.MvvmLight.ViewModelBase 41 | { 42 | private int p; 43 | 44 | public int P 45 | { 46 | get => p; 47 | set => this.Set(ref this.p, value); 48 | } 49 | } 50 | }"; 51 | 52 | RoslynAssert.Valid(Analyzer, new[] { code }, settings: MetadataReferences); 53 | } 54 | 55 | [TestCase("null")] 56 | [TestCase("string.Empty")] 57 | [TestCase(@"""P""")] 58 | [TestCase(@"nameof(P)")] 59 | [TestCase(@"nameof(this.P)")] 60 | public static void RaisePropertyChanged(string propertyName) 61 | { 62 | var code = @" 63 | namespace N 64 | { 65 | public class C : GalaSoft.MvvmLight.ViewModelBase 66 | { 67 | private int p; 68 | 69 | public int P 70 | { 71 | get { return this.p; } 72 | set 73 | { 74 | if (value == this.p) return; 75 | this.p = value; 76 | this.RaisePropertyChanged(nameof(P)); 77 | } 78 | } 79 | } 80 | }".AssertReplace(@"nameof(P)", propertyName); 81 | 82 | RoslynAssert.Valid(Analyzer, Descriptor, new[] { code }, settings: MetadataReferences); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC002MutablePublicPropertyShouldNotify/Valid.PrismBindableBase.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC002MutablePublicPropertyShouldNotify; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | using PropertyChangedAnalyzers.Test.Helpers; 6 | 7 | public partial class Valid 8 | { 9 | public static class PrismBindableBase 10 | { 11 | private static readonly Settings MetadataReferences = LibrarySettings.Prism; 12 | 13 | [Test] 14 | public static void SetProperty() 15 | { 16 | var code = @" 17 | namespace N 18 | { 19 | public class C : Microsoft.Practices.Prism.Mvvm.BindableBase 20 | { 21 | private int p; 22 | 23 | public int P 24 | { 25 | get { return p; } 26 | set { this.SetProperty(ref this.p, value); } 27 | } 28 | } 29 | }"; 30 | 31 | RoslynAssert.Valid(Analyzer, Descriptor, new[] { code }, settings: MetadataReferences); 32 | } 33 | 34 | [Test] 35 | public static void SetPropertyExpressionBodies() 36 | { 37 | var code = @" 38 | namespace N 39 | { 40 | public class C : Microsoft.Practices.Prism.Mvvm.BindableBase 41 | { 42 | private int p; 43 | 44 | public int P 45 | { 46 | get => p; 47 | set => this.SetProperty(ref this.p, value); 48 | } 49 | } 50 | }"; 51 | 52 | RoslynAssert.Valid(Analyzer, code, settings: MetadataReferences); 53 | } 54 | 55 | [TestCase("null")] 56 | [TestCase("string.Empty")] 57 | [TestCase(@"""P""")] 58 | [TestCase(@"nameof(P)")] 59 | [TestCase(@"nameof(this.P)")] 60 | public static void OnPropertyChanged(string propertyName) 61 | { 62 | var code = @" 63 | namespace N 64 | { 65 | public class C : Microsoft.Practices.Prism.Mvvm.BindableBase 66 | { 67 | private int p; 68 | 69 | public int P 70 | { 71 | get { return this.p; } 72 | set 73 | { 74 | if (value == this.p) return; 75 | this.p = value; 76 | this.OnPropertyChanged(nameof(this.P)); 77 | } 78 | } 79 | } 80 | }".AssertReplace(@"nameof(this.P)", propertyName); 81 | 82 | RoslynAssert.Valid(Analyzer, Descriptor, new[] { code }, settings: MetadataReferences); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC002MutablePublicPropertyShouldNotify/Valid.StyletMvvm.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC002MutablePublicPropertyShouldNotify; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | using PropertyChangedAnalyzers.Test.Helpers; 6 | 7 | public partial class Valid 8 | { 9 | public static class StyletMvvm 10 | { 11 | private static readonly Settings Settings = LibrarySettings.Stylet; 12 | 13 | [Test] 14 | public static void Set() 15 | { 16 | var code = @" 17 | namespace N 18 | { 19 | public class C : Stylet.PropertyChangedBase 20 | { 21 | private int p; 22 | 23 | public int P 24 | { 25 | get { return p; } 26 | set { this.SetAndNotify(ref this.p, value); } 27 | } 28 | } 29 | }"; 30 | 31 | RoslynAssert.Valid(Analyzer, Descriptor, new[] { code }, settings: Settings); 32 | } 33 | 34 | [Test] 35 | public static void SetExpressionBodies() 36 | { 37 | var code = @" 38 | namespace N 39 | { 40 | public class C : Stylet.PropertyChangedBase 41 | { 42 | private int p; 43 | 44 | public int P 45 | { 46 | get => p; 47 | set => this.SetAndNotify(ref this.p, value); 48 | } 49 | } 50 | }"; 51 | 52 | RoslynAssert.Valid(Analyzer, Descriptor, new[] { code }, settings: Settings); 53 | } 54 | 55 | [TestCase("null")] 56 | [TestCase("string.Empty")] 57 | [TestCase(@"""P""")] 58 | [TestCase(@"nameof(P)")] 59 | [TestCase(@"nameof(this.P)")] 60 | public static void RaisePropertyChanged(string propertyName) 61 | { 62 | var code = @" 63 | namespace N 64 | { 65 | public class C : Stylet.PropertyChangedBase 66 | { 67 | private int p; 68 | 69 | public int P 70 | { 71 | get { return this.p; } 72 | set 73 | { 74 | if (value == this.p) return; 75 | this.p = value; 76 | this.NotifyOfPropertyChange(nameof(P)); 77 | } 78 | } 79 | } 80 | }".AssertReplace(@"nameof(P)", propertyName); 81 | 82 | RoslynAssert.Valid(Analyzer, Descriptor, new[] { code }, settings: Settings); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC005CheckIfDifferentBeforeNotifying/CodeFix.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC005CheckIfDifferentBeforeNotifying; 2 | 3 | using Gu.Roslyn.Asserts; 4 | 5 | public static partial class CodeFix 6 | { 7 | private static readonly SetAccessorAnalyzer Analyzer = new(); 8 | private static readonly CheckIfDifferentBeforeNotifyFix Fix = new(); 9 | private static readonly ExpectedDiagnostic ExpectedDiagnostic = ExpectedDiagnostic.Create(Descriptors.INPC005CheckIfDifferentBeforeNotifying); 10 | } 11 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC008StructMustNotNotify/Diagnostics.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC008StructMustNotNotify; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | 6 | public static class Diagnostics 7 | { 8 | private static readonly StructAnalyzer Analyzer = new(); 9 | 10 | [Test] 11 | public static void WhenNotifying() 12 | { 13 | var code = @" 14 | #pragma warning disable CS0067 15 | namespace N 16 | { 17 | using System.ComponentModel; 18 | 19 | public struct S : ↓INotifyPropertyChanged 20 | { 21 | public event PropertyChangedEventHandler? PropertyChanged; 22 | } 23 | }"; 24 | RoslynAssert.Diagnostics(Analyzer, code); 25 | } 26 | 27 | [Test] 28 | public static void WhenNotifyingFullyQualified() 29 | { 30 | var code = @" 31 | #pragma warning disable CS0067 32 | namespace N 33 | { 34 | public struct S : ↓System.ComponentModel.INotifyPropertyChanged 35 | { 36 | public event System.ComponentModel.PropertyChangedEventHandler? PropertyChanged; 37 | } 38 | }"; 39 | RoslynAssert.Diagnostics(Analyzer, code); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC008StructMustNotNotify/Valid.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC008StructMustNotNotify; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | 6 | public static class Valid 7 | { 8 | private static readonly StructAnalyzer Analyzer = new(); 9 | 10 | [Test] 11 | public static void SimpleStruct() 12 | { 13 | var code = @" 14 | namespace N 15 | { 16 | public struct S 17 | { 18 | } 19 | }"; 20 | RoslynAssert.Valid(Analyzer, code); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC009NotifiesForMissingProperty/Diagnostics.Invocation.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC009NotifiesForMissingProperty; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | 6 | public static partial class Diagnostics 7 | { 8 | public static class Invocation 9 | { 10 | private static readonly InvocationAnalyzer Analyzer = new(); 11 | private static readonly ExpectedDiagnostic ExpectedDiagnostic = ExpectedDiagnostic.Create(Descriptors.INPC009NotifiesForMissingProperty); 12 | 13 | [Test] 14 | public static void CallsCallerMemberNameFromMethod() 15 | { 16 | var code = @" 17 | namespace N 18 | { 19 | using System.ComponentModel; 20 | using System.Runtime.CompilerServices; 21 | 22 | public class C : INotifyPropertyChanged 23 | { 24 | public event PropertyChangedEventHandler? PropertyChanged; 25 | 26 | public void M() 27 | { 28 | ↓this.OnPropertyChanged(); 29 | } 30 | 31 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 32 | { 33 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 34 | } 35 | } 36 | }"; 37 | 38 | RoslynAssert.Diagnostics(Analyzer, ExpectedDiagnostic, code); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC011DoNotShadow/Codefix.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC011DoNotShadow; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | 6 | public static class CodeFix 7 | { 8 | private static readonly EventAnalyzer Analyzer = new(); 9 | private static readonly RemoveShadowingFix Fix = new(); 10 | private static readonly ExpectedDiagnostic ExpectedDiagnostic = ExpectedDiagnostic.Create(Descriptors.INPC011DoNotShadow); 11 | 12 | [Test] 13 | public static void ShadowingEvent() 14 | { 15 | var viewModelBaseCode = @" 16 | namespace N.Core 17 | { 18 | using System.ComponentModel; 19 | using System.Runtime.CompilerServices; 20 | 21 | public class ViewModelBase : INotifyPropertyChanged 22 | { 23 | public event PropertyChangedEventHandler? PropertyChanged; 24 | 25 | protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null) 26 | { 27 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 28 | } 29 | } 30 | }"; 31 | 32 | var before = @" 33 | namespace N.Client 34 | { 35 | using System.ComponentModel; 36 | 37 | public class C : N.Core.ViewModelBase 38 | { 39 | ↓public event PropertyChangedEventHandler? PropertyChanged; 40 | } 41 | }"; 42 | 43 | var after = @" 44 | namespace N.Client 45 | { 46 | using System.ComponentModel; 47 | 48 | public class C : N.Core.ViewModelBase 49 | { 50 | } 51 | }"; 52 | 53 | RoslynAssert.CodeFix(Analyzer, Fix, ExpectedDiagnostic, new[] { viewModelBaseCode, before }, after); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC011DoNotShadow/Valid.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC011DoNotShadow; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | 6 | public static class Valid 7 | { 8 | private static readonly EventAnalyzer Analyzer = new(); 9 | 10 | [Test] 11 | public static void NoBaseClass() 12 | { 13 | var code = @" 14 | namespace N 15 | { 16 | using System.ComponentModel; 17 | using System.Runtime.CompilerServices; 18 | 19 | public class C : INotifyPropertyChanged 20 | { 21 | private int p; 22 | 23 | public event PropertyChangedEventHandler? PropertyChanged; 24 | 25 | public int P 26 | { 27 | get 28 | { 29 | return this.p; 30 | } 31 | 32 | set 33 | { 34 | if (value == this.p) 35 | { 36 | return; 37 | } 38 | 39 | this.p = value; 40 | this.OnPropertyChanged(nameof(P)); 41 | } 42 | } 43 | 44 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 45 | { 46 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 47 | } 48 | } 49 | }"; 50 | 51 | RoslynAssert.Valid(Analyzer, code); 52 | } 53 | 54 | [Test] 55 | public static void OverridingEvent() 56 | { 57 | var viewModelBaseCode = @" 58 | namespace N.Core 59 | { 60 | using System.ComponentModel; 61 | using System.Runtime.CompilerServices; 62 | 63 | public class ViewModelBase : INotifyPropertyChanged 64 | { 65 | public virtual event PropertyChangedEventHandler? PropertyChanged; 66 | 67 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 68 | { 69 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 70 | } 71 | } 72 | }"; 73 | 74 | var code = @" 75 | #pragma warning disable CS0067 76 | namespace N 77 | { 78 | using System.ComponentModel; 79 | 80 | public class C : N.Core.ViewModelBase 81 | { 82 | public override event PropertyChangedEventHandler? PropertyChanged; 83 | } 84 | }"; 85 | 86 | RoslynAssert.Valid(Analyzer, viewModelBaseCode, code); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC015PropertyIsRecursive/Diagnostics.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC015PropertyIsRecursive; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | 6 | public static class Diagnostics 7 | { 8 | private static readonly PropertyDeclarationAnalyzer Analyzer = new(); 9 | private static readonly ExpectedDiagnostic ExpectedDiagnostic = ExpectedDiagnostic.Create(Descriptors.INPC015PropertyIsRecursive); 10 | 11 | [Test] 12 | public static void ExpressionBody() 13 | { 14 | var code = @" 15 | namespace N 16 | { 17 | public class C 18 | { 19 | public int P => ↓this.P; 20 | } 21 | }"; 22 | 23 | RoslynAssert.Diagnostics(Analyzer, ExpectedDiagnostic, code); 24 | } 25 | 26 | [Test] 27 | public static void GetterStatementBody() 28 | { 29 | var code = @" 30 | namespace N 31 | { 32 | public class C 33 | { 34 | public int P 35 | { 36 | get { return ↓this.P; } 37 | } 38 | } 39 | }"; 40 | 41 | RoslynAssert.Diagnostics(Analyzer, ExpectedDiagnostic, code); 42 | } 43 | 44 | [Test] 45 | public static void GetterExpressionBody() 46 | { 47 | var code = @" 48 | namespace N 49 | { 50 | public class C 51 | { 52 | public int P 53 | { 54 | get => ↓this.P; 55 | } 56 | } 57 | }"; 58 | 59 | RoslynAssert.Diagnostics(Analyzer, ExpectedDiagnostic, code); 60 | } 61 | 62 | [Test] 63 | public static void SetterStatementBody() 64 | { 65 | var code = @" 66 | namespace N 67 | { 68 | public class C 69 | { 70 | private int p = 1; 71 | 72 | public int P 73 | { 74 | get { return this.p; } 75 | set { ↓this.P = value; } 76 | } 77 | } 78 | }"; 79 | 80 | RoslynAssert.Diagnostics(Analyzer, ExpectedDiagnostic, code); 81 | } 82 | 83 | [Test] 84 | public static void SetterExpressionBody() 85 | { 86 | var code = @" 87 | namespace N 88 | { 89 | public class C 90 | { 91 | private int p = 1; 92 | 93 | public int P 94 | { 95 | get => this.p; 96 | set => ↓this.P = value; 97 | } 98 | } 99 | }"; 100 | 101 | RoslynAssert.Diagnostics(Analyzer, ExpectedDiagnostic, code); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC020PreferExpressionBodyAccessor/FixAll.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC020PreferExpressionBodyAccessor; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using NUnit.Framework; 5 | 6 | public static class FixAll 7 | { 8 | private static readonly PropertyDeclarationAnalyzer Analyzer = new(); 9 | private static readonly ExpressionBodyFix Fix = new(); 10 | private static readonly ExpectedDiagnostic ExpectedDiagnostic = ExpectedDiagnostic.Create(Descriptors.INPC020PreferExpressionBodyAccessor); 11 | 12 | [Test] 13 | public static void DependencyProperty() 14 | { 15 | var before = @" 16 | namespace N 17 | { 18 | using System.Windows; 19 | using System.Windows.Controls; 20 | 21 | public class WpfControl : Control 22 | { 23 | public static readonly DependencyProperty NumberProperty = DependencyProperty.Register( 24 | nameof(Number), 25 | typeof(int), 26 | typeof(WpfControl), 27 | new PropertyMetadata(default(int))); 28 | 29 | public int Number 30 | { 31 | ↓get { return (int)this.GetValue(NumberProperty); } 32 | ↓set { this.SetValue(NumberProperty, value); } 33 | } 34 | } 35 | }"; 36 | var after = @" 37 | namespace N 38 | { 39 | using System.Windows; 40 | using System.Windows.Controls; 41 | 42 | public class WpfControl : Control 43 | { 44 | public static readonly DependencyProperty NumberProperty = DependencyProperty.Register( 45 | nameof(Number), 46 | typeof(int), 47 | typeof(WpfControl), 48 | new PropertyMetadata(default(int))); 49 | 50 | public int Number 51 | { 52 | get => (int)this.GetValue(NumberProperty); 53 | set => this.SetValue(NumberProperty, value); 54 | } 55 | } 56 | }"; 57 | 58 | RoslynAssert.FixAll(Analyzer, Fix, ExpectedDiagnostic, before, after); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/INPC023InstanceEquals/Valid.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test.INPC023InstanceEquals; 2 | 3 | using Gu.Roslyn.Asserts; 4 | using Microsoft.CodeAnalysis; 5 | using NUnit.Framework; 6 | 7 | public static class Valid 8 | { 9 | private static readonly SetAccessorAnalyzer Analyzer = new(); 10 | private static readonly DiagnosticDescriptor Descriptor = Descriptors.INPC023InstanceEquals; 11 | 12 | [Test] 13 | public static void ValueType() 14 | { 15 | var code = @" 16 | namespace N 17 | { 18 | using System.ComponentModel; 19 | using System.Runtime.CompilerServices; 20 | 21 | public class C : INotifyPropertyChanged 22 | { 23 | private int p; 24 | 25 | public event PropertyChangedEventHandler? PropertyChanged; 26 | 27 | public int P 28 | { 29 | get => this.p; 30 | set 31 | { 32 | if (value.Equals(this.p)) 33 | { 34 | return; 35 | } 36 | 37 | this.p = value; 38 | this.OnPropertyChanged(); 39 | } 40 | } 41 | 42 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 43 | { 44 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 45 | } 46 | } 47 | }"; 48 | 49 | RoslynAssert.Valid(Analyzer, Descriptor, code); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/ModuleInitializer.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test; 2 | 3 | using System.Runtime.CompilerServices; 4 | 5 | using Gu.Roslyn.Asserts; 6 | 7 | internal static class ModuleInitializer 8 | { 9 | [ModuleInitializer] 10 | internal static void Initialize() 11 | { 12 | Settings.Default = Settings.Default.WithMetadataReferences( 13 | MetadataReferences.Transitive( 14 | typeof(ModuleInitializer))); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/PropertyChangedAnalyzers.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0-windows 5 | true 6 | latest 7 | NU1701 8 | 9 | 10 | 11 | True 12 | AllEnabledByDefault 13 | latest 14 | 15 | 16 | 17 | true 18 | ../PropertyChangedAnalyzers.snk 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/PropertyChangedAnalyzers.Test.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | No 3 | True 4 | True 5 | 6 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Test/ReproBox.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers.Test; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using Gu.Roslyn.Asserts; 8 | using Microsoft.CodeAnalysis; 9 | using Microsoft.CodeAnalysis.Diagnostics; 10 | using NUnit.Framework; 11 | using PropertyChangedAnalyzers; 12 | 13 | [Ignore("For harvesting test cases only.")] 14 | public static class ReproBox 15 | { 16 | // ReSharper disable once UnusedMember.Local 17 | private static readonly IReadOnlyList AllAnalyzers = 18 | typeof(KnownSymbol) 19 | .Assembly 20 | .GetTypes() 21 | .Where(t => typeof(DiagnosticAnalyzer).IsAssignableFrom(t) && !t.IsAbstract) 22 | .Select(t => (DiagnosticAnalyzer)Activator.CreateInstance(t)) 23 | .ToArray(); 24 | 25 | private static readonly Solution Solution = CodeFactory.CreateSolution( 26 | new FileInfo("C:\\Git\\Gu.Reactive\\Gu.Reactive.sln")); 27 | 28 | [Ignore("For harvesting test cases only.")] 29 | [TestCaseSource(nameof(AllAnalyzers))] 30 | public static void SolutionRepro(DiagnosticAnalyzer analyzer) 31 | { 32 | Assert.Inconclusive("VS does not understand [Explicit]"); 33 | RoslynAssert.Valid(analyzer, Solution); 34 | } 35 | 36 | [Ignore("For harvesting test cases only.")] 37 | [TestCaseSource(nameof(AllAnalyzers))] 38 | public static void Repro(DiagnosticAnalyzer analyzer) 39 | { 40 | Assert.Inconclusive("VS does not understand [Explicit]"); 41 | var code = @" 42 | namespace N 43 | { 44 | public class C 45 | { 46 | } 47 | }"; 48 | RoslynAssert.Valid(analyzer, code); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Vsix/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Visual Studio Extension": { 4 | "executablePath": "$(DevEnvDir)devenv.exe", 5 | "commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.Vsix/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | PropertyChangedAnalyzers 6 | Roslyn analyzers INotifyPropertyChanged 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotNetAnalyzers/PropertyChangedAnalyzers/8b069923ef49dfcc76655a062521eb0c91697428/PropertyChangedAnalyzers.snk -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # Default severity for analyzer diagnostics with category 'StyleCop.CSharp.DocumentationRules' 4 | dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = none 5 | 6 | # SA0001: XML comment analysis is disabled due to project configuration 7 | dotnet_diagnostic.SA0001.severity = none 8 | 9 | # SA1401: Fields should be private 10 | dotnet_diagnostic.SA1401.severity = none 11 | 12 | # RS2008: Enable analyzer release tracking 13 | dotnet_diagnostic.RS2008.severity = none 14 | 15 | # CA1303: Do not pass literals as localized parameters 16 | dotnet_diagnostic.CA1303.severity = none 17 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/AnalyzerCategory.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | /// 4 | /// Class defining the analyzer category constants. 5 | /// 6 | internal static class AnalyzerCategory 7 | { 8 | internal const string PropertyChanged = "PropertyChangedAnalyzers.PropertyChanged"; 9 | } 10 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Analyzers/InvocationAnalyzer.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Immutable; 4 | 5 | using Gu.Roslyn.AnalyzerExtensions; 6 | 7 | using Microsoft.CodeAnalysis; 8 | using Microsoft.CodeAnalysis.CSharp; 9 | using Microsoft.CodeAnalysis.CSharp.Syntax; 10 | using Microsoft.CodeAnalysis.Diagnostics; 11 | 12 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 13 | internal class InvocationAnalyzer : DiagnosticAnalyzer 14 | { 15 | public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( 16 | Descriptors.INPC009NotifiesForMissingProperty); 17 | 18 | public override void Initialize(AnalysisContext context) 19 | { 20 | context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); 21 | context.EnableConcurrentExecution(); 22 | context.RegisterSyntaxNodeAction(c => Handle(c), SyntaxKind.InvocationExpression); 23 | } 24 | 25 | private static void Handle(SyntaxNodeAnalysisContext context) 26 | { 27 | if (!context.IsExcludedFromAnalysis() && 28 | context.Node is InvocationExpressionSyntax { ArgumentList.Arguments.Count: 0 } invocation && 29 | !(invocation.FirstAncestor() is { Keyword.ValueText: "set" }) && 30 | PropertyChanged.FindPropertyName(invocation, context.SemanticModel, context.CancellationToken) is { }) 31 | { 32 | context.ReportDiagnostic(Diagnostic.Create(Descriptors.INPC009NotifiesForMissingProperty, invocation.GetLocation())); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Analyzers/StructAnalyzer.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Immutable; 4 | using Gu.Roslyn.AnalyzerExtensions; 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.CSharp; 7 | using Microsoft.CodeAnalysis.CSharp.Syntax; 8 | using Microsoft.CodeAnalysis.Diagnostics; 9 | 10 | [DiagnosticAnalyzer(LanguageNames.CSharp)] 11 | internal class StructAnalyzer : DiagnosticAnalyzer 12 | { 13 | public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Descriptors.INPC008StructMustNotNotify); 14 | 15 | public override void Initialize(AnalysisContext context) 16 | { 17 | context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); 18 | context.EnableConcurrentExecution(); 19 | context.RegisterSyntaxNodeAction(c => Handle(c), SyntaxKind.StructDeclaration); 20 | } 21 | 22 | private static void Handle(SyntaxNodeAnalysisContext context) 23 | { 24 | if (!context.IsExcludedFromAnalysis() && 25 | context.Node is StructDeclarationSyntax { BaseList: { } baseList } declaration && 26 | context.ContainingSymbol is INamedTypeSymbol type && 27 | type.IsAssignableTo(KnownSymbol.INotifyPropertyChanged, context.Compilation)) 28 | { 29 | context.ReportDiagnostic(Diagnostic.Create(Descriptors.INPC008StructMustNotNotify, Location(), context.ContainingSymbol.Name)); 30 | 31 | Location Location() 32 | { 33 | foreach (var baseType in baseList.Types) 34 | { 35 | if (baseType == KnownSymbol.INotifyPropertyChanged) 36 | { 37 | return baseType.GetLocation(); 38 | } 39 | } 40 | 41 | return declaration.GetLocation(); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/AssemblyAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | [assembly: CLSCompliant(false)] 5 | [assembly: InternalsVisibleTo("PropertyChangedAnalyzers.CodeFixes, PublicKey=002400000480000094000000060200000024000052534131000400000100010093D9AFE01CF9ECFA136203946E0DA07D5665F494427C5938CD29B1BCFEC7449582209591398477544DAB66EFA8421AD88CB6578CA3D0BA9A951899142C87DBC9CAC41619BD91007B18CDD2BEA4EBD86AE41174112DA527497E8BF65F1D2A3618263A8D9290F797FFDFF84ADBBF859F09DB2C9F66B9618622F79D4C0761E6B58E")] 6 | [assembly: InternalsVisibleTo("PropertyChangedAnalyzers.Test, PublicKey=002400000480000094000000060200000024000052534131000400000100010093D9AFE01CF9ECFA136203946E0DA07D5665F494427C5938CD29B1BCFEC7449582209591398477544DAB66EFA8421AD88CB6578CA3D0BA9A951899142C87DBC9CAC41619BD91007B18CDD2BEA4EBD86AE41174112DA527497E8BF65F1D2A3618263A8D9290F797FFDFF84ADBBF859F09DB2C9F66B9618622F79D4C0761E6B58E")] 7 | [assembly: InternalsVisibleTo("PropertyChangedAnalyzers.Benchmarks, PublicKey=002400000480000094000000060200000024000052534131000400000100010093D9AFE01CF9ECFA136203946E0DA07D5665F494427C5938CD29B1BCFEC7449582209591398477544DAB66EFA8421AD88CB6578CA3D0BA9A951899142C87DBC9CAC41619BD91007B18CDD2BEA4EBD86AE41174112DA527497E8BF65F1D2A3618263A8D9290F797FFDFF84ADBBF859F09DB2C9F66B9618622F79D4C0761E6B58E")] 8 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/CodeFixes/ExpressionBodyFix.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Immutable; 4 | using System.Composition; 5 | using System.Threading.Tasks; 6 | using Gu.Roslyn.CodeFixExtensions; 7 | using Microsoft.CodeAnalysis; 8 | using Microsoft.CodeAnalysis.CodeFixes; 9 | using Microsoft.CodeAnalysis.CSharp.Syntax; 10 | 11 | [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(ExpressionBodyFix))] 12 | [Shared] 13 | internal class ExpressionBodyFix : DocumentEditorCodeFixProvider 14 | { 15 | public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( 16 | Descriptors.INPC020PreferExpressionBodyAccessor.Id); 17 | 18 | protected override async Task RegisterCodeFixesAsync(DocumentEditorCodeFixContext context) 19 | { 20 | var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken) 21 | .ConfigureAwait(false); 22 | foreach (var diagnostic in context.Diagnostics) 23 | { 24 | if (syntaxRoot is { } && 25 | syntaxRoot.TryFindNodeOrAncestor(diagnostic, out AccessorDeclarationSyntax? accessor) && 26 | accessor.Body is { Statements: { Count: 1 } statements } && 27 | GetExpression(statements[0]) is { } expression) 28 | { 29 | context.RegisterCodeFix( 30 | "To expression body.", 31 | (e, cancellationToken) => e.ReplaceNode( 32 | accessor, 33 | x => x.AsExpressionBody(expression.WithoutTrivia())), 34 | nameof(ExpressionBodyFix), 35 | diagnostic); 36 | } 37 | } 38 | } 39 | 40 | private static ExpressionSyntax? GetExpression(StatementSyntax statement) 41 | { 42 | return statement switch 43 | { 44 | ReturnStatementSyntax { Expression: { } expression } => expression, 45 | ExpressionStatementSyntax { Expression: { } expression } => expression, 46 | _ => null, 47 | }; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/CodeFixes/Helpers/MakePropertyNotifyHelper.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp; 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | 7 | internal static class MakePropertyNotifyHelper 8 | { 9 | internal static PropertyDeclarationSyntax WithoutInitializer(this PropertyDeclarationSyntax property) 10 | { 11 | if (property.Initializer is null) 12 | { 13 | return property; 14 | } 15 | 16 | return property.WithInitializer(null) 17 | .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) 18 | .WithTrailingTrivia(property.SemicolonToken.TrailingTrivia); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/CodeFixes/Helpers/Messages.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Linq; 4 | using Microsoft.CodeAnalysis; 5 | 6 | internal static class Messages 7 | { 8 | internal static string DisplaySignature(this IMethodSymbol method) 9 | { 10 | return $"{method.Name}({string.Join(", ", method.Parameters.Where(x => !(x.IsOptional && x.Type.Kind != SymbolKind.TypeParameter)).Select(x => $"{(x.RefKind == RefKind.Ref ? "ref " : string.Empty)}{x.Name}"))})"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/CodeFixes/MakeProtectedFix.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Immutable; 4 | using System.Composition; 5 | using System.Threading.Tasks; 6 | using Gu.Roslyn.CodeFixExtensions; 7 | using Microsoft.CodeAnalysis; 8 | using Microsoft.CodeAnalysis.CodeFixes; 9 | using Microsoft.CodeAnalysis.CSharp; 10 | using Microsoft.CodeAnalysis.CSharp.Syntax; 11 | 12 | [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MakeProtectedFix))] 13 | [Shared] 14 | internal class MakeProtectedFix : DocumentEditorCodeFixProvider 15 | { 16 | public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( 17 | Descriptors.INPC018InvokerShouldBeProtected.Id); 18 | 19 | protected override async Task RegisterCodeFixesAsync(DocumentEditorCodeFixContext context) 20 | { 21 | var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken) 22 | .ConfigureAwait(false); 23 | foreach (var diagnostic in context.Diagnostics) 24 | { 25 | if (syntaxRoot?.FindToken(diagnostic.Location.SourceSpan.Start) is { } token) 26 | { 27 | if (token.IsKind(SyntaxKind.PrivateKeyword)) 28 | { 29 | context.RegisterCodeFix( 30 | $"Change to: protected.", 31 | (editor, _) => editor.ReplaceToken( 32 | token, 33 | SyntaxFactory.Token(SyntaxKind.ProtectedKeyword)), 34 | nameof(MakeProtectedFix), 35 | diagnostic); 36 | } 37 | else if (token.IsKind(SyntaxKind.IdentifierToken) && 38 | token.Parent is MethodDeclarationSyntax methodDeclaration) 39 | { 40 | context.RegisterCodeFix( 41 | $"Make protected.", 42 | (editor, _) => editor.ReplaceNode( 43 | methodDeclaration, 44 | methodDeclaration.WithModifiers(methodDeclaration.Modifiers.Insert(0, SyntaxFactory.Token(SyntaxKind.ProtectedKeyword)))), 45 | nameof(MakeProtectedFix), 46 | diagnostic); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/CodeFixes/RemoveShadowingFix.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Immutable; 4 | using System.Composition; 5 | using System.Threading.Tasks; 6 | using Gu.Roslyn.CodeFixExtensions; 7 | using Microsoft.CodeAnalysis; 8 | using Microsoft.CodeAnalysis.CodeFixes; 9 | using Microsoft.CodeAnalysis.CSharp.Syntax; 10 | 11 | [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(RemoveShadowingFix))] 12 | [Shared] 13 | internal class RemoveShadowingFix : DocumentEditorCodeFixProvider 14 | { 15 | public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(Descriptors.INPC011DoNotShadow.Id); 16 | 17 | protected override async Task RegisterCodeFixesAsync(DocumentEditorCodeFixContext context) 18 | { 19 | var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken) 20 | .ConfigureAwait(false); 21 | 22 | foreach (var diagnostic in context.Diagnostics) 23 | { 24 | if (syntaxRoot?.FindNode(diagnostic.Location.SourceSpan) is MemberDeclarationSyntax eventDeclaration) 25 | { 26 | context.RegisterCodeFix( 27 | "Remove shadowing event.", 28 | (editor, _) => editor.RemoveNode(eventDeclaration), 29 | nameof(RemoveShadowingFix), 30 | diagnostic); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/CodeFixes/ReplaceExpressionFix.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Immutable; 4 | using System.Composition; 5 | using System.Threading.Tasks; 6 | using Gu.Roslyn.AnalyzerExtensions; 7 | using Gu.Roslyn.CodeFixExtensions; 8 | using Microsoft.CodeAnalysis; 9 | using Microsoft.CodeAnalysis.CodeFixes; 10 | using Microsoft.CodeAnalysis.CSharp.Syntax; 11 | 12 | [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(ReplaceExpressionFix))] 13 | [Shared] 14 | internal class ReplaceExpressionFix : DocumentEditorCodeFixProvider 15 | { 16 | public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( 17 | Descriptors.INPC019GetBackingField.Id, 18 | Descriptors.INPC022EqualToBackingField.Id); 19 | 20 | protected override async Task RegisterCodeFixesAsync(DocumentEditorCodeFixContext context) 21 | { 22 | var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken) 23 | .ConfigureAwait(false); 24 | 25 | foreach (var diagnostic in context.Diagnostics) 26 | { 27 | if (syntaxRoot is { } && 28 | syntaxRoot.TryFindNode(diagnostic, out ExpressionSyntax? expression) && 29 | diagnostic.AdditionalLocations.TrySingle(out var additionalLocation) && 30 | syntaxRoot.TryFindNode(additionalLocation, out ExpressionSyntax? fieldAccess)) 31 | { 32 | context.RegisterCodeFix( 33 | $"Use: {fieldAccess}", 34 | (editor, _) => editor.ReplaceNode( 35 | expression, 36 | x => fieldAccess.WithTriviaFrom(x)), 37 | diagnostic.Descriptor.Id, 38 | diagnostic); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/CodeFixes/SetBackingFieldFix.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Immutable; 4 | using System.Composition; 5 | using System.Threading.Tasks; 6 | using Gu.Roslyn.AnalyzerExtensions; 7 | using Gu.Roslyn.CodeFixExtensions; 8 | using Microsoft.CodeAnalysis; 9 | using Microsoft.CodeAnalysis.CodeFixes; 10 | using Microsoft.CodeAnalysis.CSharp.Syntax; 11 | 12 | [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(SetBackingFieldFix))] 13 | [Shared] 14 | internal class SetBackingFieldFix : DocumentEditorCodeFixProvider 15 | { 16 | public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(Descriptors.INPC014SetBackingFieldInConstructor.Id); 17 | 18 | protected override async Task RegisterCodeFixesAsync(DocumentEditorCodeFixContext context) 19 | { 20 | var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken) 21 | .ConfigureAwait(false); 22 | 23 | foreach (var diagnostic in context.Diagnostics) 24 | { 25 | if (syntaxRoot?.FindNode(diagnostic.Location.SourceSpan) is AssignmentExpressionSyntax assignment && 26 | diagnostic.AdditionalLocations.TrySingle(out var additionalLocation) && 27 | syntaxRoot.FindNode(additionalLocation.SourceSpan) is ExpressionSyntax fieldAccess) 28 | { 29 | context.RegisterCodeFix( 30 | "Set backing field.", 31 | (e, cancellationToken) => e.ReplaceNode( 32 | assignment.Left, 33 | (x, _) => Qualify(fieldAccess).WithTriviaFrom(x)), 34 | nameof(SetBackingFieldFix), 35 | diagnostic); 36 | 37 | ExpressionSyntax Qualify(ExpressionSyntax expression) 38 | { 39 | if (expression is IdentifierNameSyntax identifierName && 40 | (Scope.HasParameter(assignment, identifierName.Identifier.ValueText) || 41 | Scope.HasLocal(assignment, identifierName.Identifier.ValueText))) 42 | { 43 | return InpcFactory.SymbolAccess(identifierName.Identifier.ValueText, CodeStyleResult.Yes); 44 | } 45 | 46 | return expression; 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/CodeFixes/UseNameofFix.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Immutable; 4 | using System.Composition; 5 | using System.Threading.Tasks; 6 | using Gu.Roslyn.AnalyzerExtensions; 7 | using Gu.Roslyn.CodeFixExtensions; 8 | using Microsoft.CodeAnalysis; 9 | using Microsoft.CodeAnalysis.CodeFixes; 10 | using Microsoft.CodeAnalysis.CSharp.Syntax; 11 | 12 | [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseNameofFix))] 13 | [Shared] 14 | internal class UseNameofFix : DocumentEditorCodeFixProvider 15 | { 16 | public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(Descriptors.INPC013UseNameof.Id); 17 | 18 | protected override async Task RegisterCodeFixesAsync(DocumentEditorCodeFixContext context) 19 | { 20 | var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken) 21 | .ConfigureAwait(false); 22 | var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken) 23 | .ConfigureAwait(false); 24 | foreach (var diagnostic in context.Diagnostics) 25 | { 26 | if (syntaxRoot?.FindNode(diagnostic.Location.SourceSpan) is ArgumentSyntax { Expression: LiteralExpressionSyntax literal } argument && 27 | semanticModel is { } && 28 | semanticModel.LookupSymbols(argument.SpanStart, name: literal.Token.ValueText).TryFirst(out var member)) 29 | { 30 | context.RegisterCodeFix( 31 | "Use nameof", 32 | async (editor, cancellationToken) => 33 | { 34 | var replacement = await editor.SymbolAccessAsync(member, literal, cancellationToken) 35 | .ConfigureAwait(false); 36 | _ = editor.ReplaceNode( 37 | literal, 38 | x => InpcFactory.Nameof(replacement).WithTriviaFrom(x)); 39 | }, 40 | nameof(UseNameofFix), 41 | diagnostic); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/AnalysisResult.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | public enum AnalysisResult 4 | { 5 | /// 6 | /// Analysis determined that PropertyChanged was not invoked in this path. 7 | /// 8 | No, 9 | 10 | /// 11 | /// Analysis determined that PropertyChanged was invoked in this path. 12 | /// 13 | Yes, 14 | 15 | /// 16 | /// Analysis determined that PropertyChanged is potentially invoked in this path. 17 | /// 18 | Maybe, 19 | } 20 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/AnalysisResult{T}.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | internal readonly struct AnalysisResult 4 | { 5 | internal readonly AnalysisResult Result; 6 | 7 | internal readonly T Value; 8 | 9 | internal AnalysisResult(AnalysisResult result, T value) 10 | { 11 | this.Result = result; 12 | this.Value = value; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/BackingMemberAndValue.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Diagnostics; 4 | using Microsoft.CodeAnalysis.CSharp.Syntax; 5 | 6 | [DebuggerDisplay("Member: {this.Member.GetText().ToString()} Parameter:{this.Parameter.GetText().ToString()},nq")] 7 | internal readonly struct BackingMemberAndValue 8 | { 9 | internal readonly ExpressionSyntax Member; 10 | 11 | internal readonly IdentifierNameSyntax Parameter; 12 | 13 | internal BackingMemberAndValue(ExpressionSyntax member, IdentifierNameSyntax parameter) 14 | { 15 | this.Member = member; 16 | this.Parameter = parameter; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/CallerMemberNameAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Microsoft.CodeAnalysis; 4 | 5 | internal static class CallerMemberNameAttribute 6 | { 7 | internal static bool IsAvailable(SemanticModel semanticModel) 8 | { 9 | return semanticModel.Compilation.GetTypeByMetadataName(KnownSymbol.CallerMemberNameAttribute.FullName) != null; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/Comparers/SyntaxTokenComparer.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Generic; 4 | using Microsoft.CodeAnalysis; 5 | 6 | internal sealed class SyntaxTokenComparer : IEqualityComparer 7 | { 8 | internal static readonly SyntaxTokenComparer ByValueText = new(); 9 | 10 | private SyntaxTokenComparer() 11 | { 12 | } 13 | 14 | bool IEqualityComparer.Equals(SyntaxToken x, SyntaxToken y) => Equals(x, y); 15 | 16 | public int GetHashCode(SyntaxToken obj) => obj.ValueText.GetHashCode(); 17 | 18 | internal static bool Equals(SyntaxToken x, SyntaxToken y) => x.ValueText == y.ValueText; 19 | } 20 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/Getter.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | public static class Getter 6 | { 7 | internal static ExpressionSyntax? FindSingleReturned(AccessorDeclarationSyntax getter) 8 | { 9 | return getter switch 10 | { 11 | { ExpressionBody.Expression: { } expression } => expression, 12 | { Body.Statements: { Count: 1 } statements } 13 | when statements[0] is ReturnStatementSyntax returnStatement 14 | => returnStatement.Expression, 15 | { Body: { } body } => ReturnExpressionsWalker.TryGetSingle(body, out var result) ? result : null, 16 | _ => null, 17 | }; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/CaliburnMicroPropertyChangedBase.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class CaliburnMicroPropertyChangedBase : QualifiedType 6 | { 7 | internal readonly QualifiedMethod Set; 8 | internal readonly QualifiedMethod NotifyOfPropertyChange; 9 | internal readonly QualifiedMethod NotifyOfPropertyChangeOfT; 10 | 11 | internal CaliburnMicroPropertyChangedBase() 12 | : base("Caliburn.Micro.PropertyChangedBase") 13 | { 14 | this.Set = new QualifiedMethod(this, nameof(this.Set)); 15 | this.NotifyOfPropertyChange = new QualifiedMethod(this, nameof(this.NotifyOfPropertyChange)); 16 | this.NotifyOfPropertyChangeOfT = new QualifiedMethod(this, $"{nameof(this.NotifyOfPropertyChange)}`1"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/DependencyObjectType.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class DependencyObjectType : QualifiedType 6 | { 7 | internal readonly QualifiedMethod GetValue; 8 | internal readonly QualifiedMethod SetValue; 9 | internal readonly QualifiedMethod SetCurrentValue; 10 | 11 | internal DependencyObjectType() 12 | : base("System.Windows.DependencyObject") 13 | { 14 | this.GetValue = new QualifiedMethod(this, nameof(this.GetValue)); 15 | this.SetValue = new QualifiedMethod(this, nameof(this.SetValue)); 16 | this.SetCurrentValue = new QualifiedMethod(this, nameof(this.SetCurrentValue)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/EqualityComparerOfTType.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class EqualityComparerOfTType : QualifiedType 6 | { 7 | internal readonly QualifiedMethod EqualsMethod; 8 | 9 | internal EqualityComparerOfTType() 10 | : base("System.Collections.Generic.EqualityComparer`1") 11 | { 12 | this.EqualsMethod = new QualifiedMethod(this, "Equals"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/INotifyPropertyChangedType.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class INotifyPropertyChangedType : QualifiedType 6 | { 7 | internal readonly QualifiedEvent PropertyChanged; 8 | 9 | internal INotifyPropertyChangedType() 10 | : base("System.ComponentModel.INotifyPropertyChanged") 11 | { 12 | this.PropertyChanged = new QualifiedEvent(this, nameof(this.PropertyChanged)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/MicrosoftPracticesPrismMvvmBindableBase.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class MicrosoftPracticesPrismMvvmBindableBase : QualifiedType 6 | { 7 | internal readonly QualifiedMethod SetProperty; 8 | internal readonly QualifiedMethod OnPropertyChanged; 9 | internal readonly QualifiedMethod OnPropertyChangedOfT; 10 | 11 | internal MicrosoftPracticesPrismMvvmBindableBase() 12 | : base("Microsoft.Practices.Prism.Mvvm.BindableBase") 13 | { 14 | this.SetProperty = new QualifiedMethod(this, nameof(this.SetProperty)); 15 | this.OnPropertyChanged = new QualifiedMethod(this, nameof(this.OnPropertyChanged)); 16 | this.OnPropertyChangedOfT = new QualifiedMethod(this, $"{nameof(this.OnPropertyChanged)}`1"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/MvvmCrossCoreMvxNotifyPropertyChanged.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class MvvmCrossCoreMvxNotifyPropertyChanged : QualifiedType 6 | { 7 | internal readonly QualifiedMethod SetProperty; 8 | internal readonly QualifiedMethod RaisePropertyChanged; 9 | internal readonly QualifiedMethod RaisePropertyChangedOfT; 10 | 11 | internal MvvmCrossCoreMvxNotifyPropertyChanged() 12 | : base("MvvmCross.Core.ViewModels.MvxNotifyPropertyChanged") 13 | { 14 | this.SetProperty = new QualifiedMethod(this, nameof(this.SetProperty)); 15 | this.RaisePropertyChanged = new QualifiedMethod(this, nameof(this.RaisePropertyChanged)); 16 | this.RaisePropertyChangedOfT = new QualifiedMethod(this, $"{nameof(this.RaisePropertyChanged)}`1"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/MvvmCrossMvxNotifyPropertyChanged.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class MvvmCrossMvxNotifyPropertyChanged : QualifiedType 6 | { 7 | internal readonly QualifiedMethod SetProperty; 8 | internal readonly QualifiedMethod RaisePropertyChanged; 9 | internal readonly QualifiedMethod RaisePropertyChangedOfT; 10 | 11 | internal MvvmCrossMvxNotifyPropertyChanged() 12 | : base("MvvmCross.ViewModels.MvxNotifyPropertyChanged") 13 | { 14 | this.SetProperty = new QualifiedMethod(this, nameof(this.SetProperty)); 15 | this.RaisePropertyChanged = new QualifiedMethod(this, nameof(this.RaisePropertyChanged)); 16 | this.RaisePropertyChangedOfT = new QualifiedMethod(this, $"{nameof(this.RaisePropertyChanged)}`1"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/MvvmLightObservableObject.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class MvvmLightObservableObject : QualifiedType 6 | { 7 | internal readonly QualifiedMethod Set; 8 | internal readonly QualifiedMethod RaisePropertyChanged; 9 | internal readonly QualifiedMethod RaisePropertyChangedOfT; 10 | 11 | internal MvvmLightObservableObject() 12 | : base("GalaSoft.MvvmLight.ObservableObject") 13 | { 14 | this.Set = new QualifiedMethod(this, nameof(this.Set)); 15 | this.RaisePropertyChanged = new QualifiedMethod(this, nameof(this.RaisePropertyChanged)); 16 | this.RaisePropertyChangedOfT = new QualifiedMethod(this, $"{nameof(this.RaisePropertyChanged)}`1"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/MvvmLightViewModelBase.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class MvvmLightViewModelBase : QualifiedType 6 | { 7 | internal readonly QualifiedMethod Set; 8 | internal readonly QualifiedMethod RaisePropertyChanged; 9 | internal readonly QualifiedMethod RaisePropertyChangedOfT; 10 | 11 | internal MvvmLightViewModelBase() 12 | : base("GalaSoft.MvvmLight.ViewModelBase") 13 | { 14 | this.Set = new QualifiedMethod(this, nameof(this.Set)); 15 | this.RaisePropertyChanged = new QualifiedMethod(this, nameof(this.RaisePropertyChanged)); 16 | this.RaisePropertyChangedOfT = new QualifiedMethod(this, $"{nameof(this.RaisePropertyChanged)}`1"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/NullableOfTType.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class NullableOfTType : QualifiedType 6 | { 7 | internal NullableOfTType() 8 | : base("System.Nullable`1") 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/NullableType.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class NullableType : QualifiedType 6 | { 7 | internal new readonly QualifiedMethod Equals; 8 | 9 | internal NullableType() 10 | : base("System.Nullable") 11 | { 12 | this.Equals = new QualifiedMethod(this, nameof(this.Equals)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/ObjectType.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class ObjectType : QualifiedType 6 | { 7 | internal new readonly QualifiedMethod Equals; 8 | internal new readonly QualifiedMethod ReferenceEquals; 9 | 10 | internal ObjectType() 11 | : base("System.Object", "object") 12 | { 13 | this.Equals = new QualifiedMethod(this, nameof(this.Equals)); 14 | this.ReferenceEquals = new QualifiedMethod(this, nameof(this.ReferenceEquals)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/PropertyChangedEventHandlerType.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class PropertyChangedEventHandlerType : QualifiedType 6 | { 7 | internal readonly QualifiedMethod Invoke; 8 | 9 | internal PropertyChangedEventHandlerType() 10 | : base("System.ComponentModel.PropertyChangedEventHandler") 11 | { 12 | this.Invoke = new QualifiedMethod(this, nameof(this.Invoke)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/StringType.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class StringType : QualifiedType 6 | { 7 | internal readonly QualifiedField Empty; 8 | internal new readonly QualifiedMethod Equals; 9 | 10 | internal StringType() 11 | : base("System.String", "string") 12 | { 13 | this.Empty = new QualifiedField(this, nameof(this.Empty)); 14 | this.Equals = new QualifiedMethod(this, nameof(this.Equals)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/KnownSymbols/StyletPropertyChangedBase.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | internal class StyletPropertyChangedBase : QualifiedType 6 | { 7 | internal readonly QualifiedMethod SetAndNotify; 8 | internal readonly QualifiedMethod NotifyOfPropertyChange; 9 | internal readonly QualifiedMethod NotifyOfPropertyChangeOfT; 10 | 11 | internal StyletPropertyChangedBase() 12 | : base("Stylet.PropertyChangedBase") 13 | { 14 | this.SetAndNotify = new QualifiedMethod(this, nameof(this.SetAndNotify)); 15 | this.NotifyOfPropertyChange = new QualifiedMethod(this, nameof(this.NotifyOfPropertyChange)); 16 | this.NotifyOfPropertyChangeOfT = new QualifiedMethod(this, $"{nameof(this.NotifyOfPropertyChange)}`1"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/MutableAutoProperty.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | 7 | internal readonly struct MutableAutoProperty 8 | { 9 | internal readonly AccessorDeclarationSyntax Getter; 10 | internal readonly AccessorDeclarationSyntax Setter; 11 | 12 | private MutableAutoProperty(AccessorDeclarationSyntax getter, AccessorDeclarationSyntax setter) 13 | { 14 | this.Getter = getter; 15 | this.Setter = setter; 16 | } 17 | 18 | internal static MutableAutoProperty? Match(PropertyDeclarationSyntax candidate) 19 | { 20 | if (candidate.Getter() is { ExpressionBody: null, Body: null } getter && 21 | candidate.Setter() is { ExpressionBody: null, Body: null } setter) 22 | { 23 | return new MutableAutoProperty(getter, setter); 24 | } 25 | 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/MutableProperty.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Gu.Roslyn.AnalyzerExtensions; 4 | 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | 7 | internal readonly struct MutableProperty 8 | { 9 | internal readonly AccessorDeclarationSyntax Getter; 10 | internal readonly AccessorDeclarationSyntax Setter; 11 | 12 | private MutableProperty(AccessorDeclarationSyntax getter, AccessorDeclarationSyntax setter) 13 | { 14 | this.Getter = getter; 15 | this.Setter = setter; 16 | } 17 | 18 | internal static MutableProperty? Match(PropertyDeclarationSyntax candidate) 19 | { 20 | if (candidate.Getter() is { } getter && 21 | candidate.Setter() is { } setter) 22 | { 23 | return new MutableProperty(getter, setter); 24 | } 25 | 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/PropertyNameArgument.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Threading; 4 | using Gu.Roslyn.AnalyzerExtensions; 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | 8 | internal readonly struct PropertyNameArgument 9 | { 10 | internal readonly ArgumentSyntax? Argument; 11 | internal readonly string? Name; 12 | 13 | internal PropertyNameArgument(ArgumentSyntax? argument, string? name) 14 | { 15 | this.Argument = argument; 16 | this.Name = name; 17 | } 18 | 19 | internal static PropertyNameArgument? Match(ArgumentSyntax argument, SemanticModel semanticModel, CancellationToken cancellationToken) 20 | { 21 | return argument.TryGetStringValue(semanticModel, cancellationToken, out var name) 22 | ? new PropertyNameArgument(argument, name) 23 | : (PropertyNameArgument?)null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/SyntaxtTreeHelpers/FieldDeclarationSyntaxExt.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System; 4 | using Gu.Roslyn.AnalyzerExtensions; 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | 7 | internal static class FieldDeclarationSyntaxExt 8 | { 9 | internal static string Name(this FieldDeclarationSyntax declaration) 10 | { 11 | if (declaration?.Declaration is { } variableDeclaration && 12 | variableDeclaration.Variables.TrySingle(out var variable)) 13 | { 14 | return variable.Identifier.Text; 15 | } 16 | 17 | throw new InvalidOperationException($"Could not get name of field {declaration}"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/SyntaxtTreeHelpers/IfStatementSyntaxExt.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using Microsoft.CodeAnalysis.CSharp.Syntax; 4 | 5 | internal static class IfStatementSyntaxExt 6 | { 7 | internal static bool IsReturnOnly(this IfStatementSyntax ifStatement) 8 | { 9 | return ifStatement.Statement switch 10 | { 11 | ReturnStatementSyntax _ => true, 12 | BlockSyntax { Statements: { } statements } 13 | => statements.Last() is ReturnStatementSyntax, 14 | _ => false, 15 | }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/TrySetMatch.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | internal readonly struct TrySetMatch 6 | { 7 | internal readonly AnalysisResult AnalysisResult; 8 | 9 | internal readonly T Field; 10 | 11 | internal readonly T Value; 12 | 13 | [AllowNull] 14 | internal readonly T Name; 15 | 16 | internal TrySetMatch(AnalysisResult analysisResult, T field, T value, [AllowNull]T name) 17 | { 18 | this.AnalysisResult = analysisResult; 19 | this.Field = field; 20 | this.Value = value; 21 | this.Name = name; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/Walkers/AssignmentWalker.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Generic; 4 | using Gu.Roslyn.AnalyzerExtensions; 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | 8 | internal sealed class AssignmentWalker : PooledWalker 9 | { 10 | private readonly List assignments = new(); 11 | 12 | private AssignmentWalker() 13 | { 14 | } 15 | 16 | /// 17 | /// Gets a list with all in the scope. 18 | /// 19 | internal IReadOnlyList Assignments => this.assignments; 20 | 21 | public override void VisitAssignmentExpression(AssignmentExpressionSyntax node) 22 | { 23 | this.assignments.Add(node); 24 | base.VisitAssignmentExpression(node); 25 | } 26 | 27 | internal static AssignmentWalker Borrow(SyntaxNode node) => BorrowAndVisit(node, () => new AssignmentWalker()); 28 | 29 | protected override void Clear() 30 | { 31 | this.assignments.Clear(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/Walkers/IdentifierNameWalker.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using Gu.Roslyn.AnalyzerExtensions; 6 | using Microsoft.CodeAnalysis; 7 | using Microsoft.CodeAnalysis.CSharp.Syntax; 8 | 9 | internal sealed class IdentifierNameWalker : PooledWalker 10 | { 11 | private readonly List identifierNames = new(); 12 | 13 | private IdentifierNameWalker() 14 | { 15 | } 16 | 17 | internal IReadOnlyList IdentifierNames => this.identifierNames; 18 | 19 | public override void VisitIdentifierName(IdentifierNameSyntax node) 20 | { 21 | this.identifierNames.Add(node); 22 | base.VisitIdentifierName(node); 23 | } 24 | 25 | internal static IdentifierNameWalker Borrow(SyntaxNode node) => BorrowAndVisit(node, () => new IdentifierNameWalker()); 26 | 27 | internal static bool Contains(SyntaxNode node, IParameterSymbol parameter, SemanticModel semanticModel, CancellationToken cancellationToken) 28 | { 29 | using var walker = Borrow(node); 30 | foreach (var identifierName in walker.identifierNames) 31 | { 32 | if (parameter.MetadataName == identifierName.Identifier.ValueText && 33 | semanticModel.TryGetSymbol(identifierName, cancellationToken, out IParameterSymbol _)) 34 | { 35 | return true; 36 | } 37 | } 38 | 39 | return false; 40 | } 41 | 42 | protected override void Clear() 43 | { 44 | this.identifierNames.Clear(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/Walkers/IdentifierTypeWalker.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Generic; 4 | using Gu.Roslyn.AnalyzerExtensions; 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | 7 | internal sealed class IdentifierTypeWalker : PooledWalker 8 | { 9 | private readonly List parameters = new(); 10 | private readonly List locals = new(); 11 | 12 | public override void VisitParameter(ParameterSyntax node) 13 | { 14 | this.parameters.Add(node); 15 | base.VisitParameter(node); 16 | } 17 | 18 | public override void VisitVariableDeclarator(VariableDeclaratorSyntax node) 19 | { 20 | this.locals.Add(node); 21 | base.VisitVariableDeclarator(node); 22 | } 23 | 24 | internal static bool IsLocalOrParameter(IdentifierNameSyntax candidate) 25 | { 26 | return candidate switch 27 | { 28 | { Parent: MemberAccessExpressionSyntax _ } => false, 29 | { Identifier.ValueText: "value" } 30 | when candidate.FirstAncestor() is { Keyword.ValueText: "set" } 31 | => true, 32 | _ => Walk(), 33 | }; 34 | 35 | bool Walk() 36 | { 37 | if (candidate.FirstAncestor() is { } member) 38 | { 39 | using var walker = BorrowAndVisit(member, () => new IdentifierTypeWalker()); 40 | foreach (var parameter in walker.parameters) 41 | { 42 | if (candidate.Identifier.ValueText == parameter.Identifier.ValueText) 43 | { 44 | return true; 45 | } 46 | } 47 | 48 | foreach (var declarator in walker.locals) 49 | { 50 | if (candidate.Identifier.ValueText == declarator.Identifier.ValueText) 51 | { 52 | return true; 53 | } 54 | } 55 | } 56 | 57 | return false; 58 | } 59 | } 60 | 61 | protected override void Clear() 62 | { 63 | this.parameters.Clear(); 64 | this.locals.Clear(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/Walkers/InvocationWalker.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Generic; 4 | using Gu.Roslyn.AnalyzerExtensions; 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | 8 | internal sealed class InvocationWalker : PooledWalker 9 | { 10 | private readonly List invocations = new(); 11 | 12 | private InvocationWalker() 13 | { 14 | } 15 | 16 | internal IReadOnlyList Invocations => this.invocations; 17 | 18 | public override void VisitInvocationExpression(InvocationExpressionSyntax node) 19 | { 20 | this.invocations.Add(node); 21 | base.VisitInvocationExpression(node); 22 | } 23 | 24 | internal static InvocationWalker Borrow(SyntaxNode node) => BorrowAndVisit(node, () => new InvocationWalker()); 25 | 26 | protected override void Clear() 27 | { 28 | this.invocations.Clear(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/Helpers/Walkers/ReturnExpressionsWalker.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChangedAnalyzers; 2 | 3 | using System.Collections.Generic; 4 | using System.Diagnostics.CodeAnalysis; 5 | using Gu.Roslyn.AnalyzerExtensions; 6 | using Microsoft.CodeAnalysis; 7 | using Microsoft.CodeAnalysis.CSharp.Syntax; 8 | 9 | internal sealed class ReturnExpressionsWalker : PooledWalker 10 | { 11 | private readonly List returnValues = new(); 12 | 13 | private ReturnExpressionsWalker() 14 | { 15 | } 16 | 17 | internal IReadOnlyList ReturnValues => this.returnValues; 18 | 19 | public override void VisitReturnStatement(ReturnStatementSyntax node) 20 | { 21 | if (node.Expression is { }) 22 | { 23 | this.returnValues.Add(node.Expression); 24 | } 25 | 26 | base.VisitReturnStatement(node); 27 | } 28 | 29 | public override void VisitArrowExpressionClause(ArrowExpressionClauseSyntax node) 30 | { 31 | if (!node.TryFirstAncestor(out _)) 32 | { 33 | this.returnValues.Add(node.Expression); 34 | base.VisitArrowExpressionClause(node); 35 | } 36 | } 37 | 38 | internal static ReturnExpressionsWalker Empty() => Borrow(() => new ReturnExpressionsWalker()); 39 | 40 | internal static ReturnExpressionsWalker Borrow(SyntaxNode node) => BorrowAndVisit(node, () => new ReturnExpressionsWalker()); 41 | 42 | internal static bool TryGetSingle(SyntaxNode node, [NotNullWhen(true)] out ExpressionSyntax? returnValue) 43 | { 44 | using var walker = Borrow(node); 45 | return walker.returnValues.TrySingle(out returnValue!); 46 | } 47 | 48 | protected override void Clear() 49 | { 50 | this.returnValues.Clear(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/tools/install.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve 4 | 5 | foreach($analyzersPath in $analyzersPaths) 6 | { 7 | # Install the language agnostic analyzers. 8 | if (Test-Path $analyzersPath) 9 | { 10 | foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll) 11 | { 12 | if($project.Object.AnalyzerReferences) 13 | { 14 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) 15 | } 16 | } 17 | } 18 | } 19 | 20 | $project.Type # gives the language name like (C# or VB.NET) 21 | $languageFolder = "" 22 | if($project.Type -eq "C#") 23 | { 24 | $languageFolder = "cs" 25 | } 26 | if($project.Type -eq "VB.NET") 27 | { 28 | $languageFolder = "vb" 29 | } 30 | if($languageFolder -eq "") 31 | { 32 | return 33 | } 34 | 35 | foreach($analyzersPath in $analyzersPaths) 36 | { 37 | # Install language specific analyzers. 38 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder 39 | if (Test-Path $languageAnalyzersPath) 40 | { 41 | foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll) 42 | { 43 | if($project.Object.AnalyzerReferences) 44 | { 45 | $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /PropertyChangedAnalyzers/tools/uninstall.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | $analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve 4 | 5 | foreach($analyzersPath in $analyzersPaths) 6 | { 7 | # Uninstall the language agnostic analyzers. 8 | if (Test-Path $analyzersPath) 9 | { 10 | foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll) 11 | { 12 | if($project.Object.AnalyzerReferences) 13 | { 14 | $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) 15 | } 16 | } 17 | } 18 | } 19 | 20 | $project.Type # gives the language name like (C# or VB.NET) 21 | $languageFolder = "" 22 | if($project.Type -eq "C#") 23 | { 24 | $languageFolder = "cs" 25 | } 26 | if($project.Type -eq "VB.NET") 27 | { 28 | $languageFolder = "vb" 29 | } 30 | if($languageFolder -eq "") 31 | { 32 | return 33 | } 34 | 35 | foreach($analyzersPath in $analyzersPaths) 36 | { 37 | # Uninstall language specific analyzers. 38 | $languageAnalyzersPath = join-path $analyzersPath $languageFolder 39 | if (Test-Path $languageAnalyzersPath) 40 | { 41 | foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll) 42 | { 43 | if($project.Object.AnalyzerReferences) 44 | { 45 | try 46 | { 47 | $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) 48 | } 49 | catch 50 | { 51 | 52 | } 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /ValidCode/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | dotnet_diagnostic.IDE0009.severity = none 3 | dotnet_diagnostic.IDE0052.severity = none 4 | dotnet_diagnostic.IDE0079.severity = none 5 | dotnet_diagnostic.IDE1006.severity = none 6 | 7 | dotnet_diagnostic.CA1816.severity = none 8 | dotnet_diagnostic.CA1822.severity = none 9 | dotnet_diagnostic.CA2011.severity = none 10 | -------------------------------------------------------------------------------- /ValidCode/CachingInConcurrentDictionary.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode; 3 | 4 | using System.Collections.Concurrent; 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public class CachingInConcurrentDictionary : INotifyPropertyChanged 9 | { 10 | private static readonly ConcurrentDictionary Cache = new(); 11 | 12 | public event PropertyChangedEventHandler? PropertyChanged; 13 | 14 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 15 | { 16 | this.PropertyChanged?.Invoke(this, Cache.GetOrAdd(propertyName ?? string.Empty, name => new PropertyChangedEventArgs(name))); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ValidCode/CustomControl.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode; 3 | 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | 7 | public class CustomControl : Control 8 | { 9 | public static readonly DependencyProperty Value1Property = DependencyProperty.Register( 10 | nameof(Value1), 11 | typeof(int), 12 | typeof(CustomControl), 13 | new PropertyMetadata(default(int))); 14 | 15 | public static readonly DependencyProperty Value2Property = DependencyProperty.Register( 16 | nameof(Value2), 17 | typeof(int), 18 | typeof(CustomControl), 19 | new PropertyMetadata(default(int))); 20 | 21 | public int Value1 22 | { 23 | #pragma warning disable INPC020 // Prefer expression body accessor. 24 | get { return (int)this.GetValue(Value1Property); } 25 | set { this.SetValue(Value1Property, value); } 26 | #pragma warning restore INPC020 // Prefer expression body accessor. 27 | } 28 | 29 | public int Value2 30 | { 31 | get => (int)this.GetValue(Value2Property); 32 | set => this.SetValue(Value2Property, value); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ValidCode/DisposableFoo.cs: -------------------------------------------------------------------------------- 1 | namespace ValidCode; 2 | 3 | using System; 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public sealed class DisposableFoo : INotifyPropertyChanged, IDisposable 8 | { 9 | private bool disposed; 10 | private string? name; 11 | 12 | public event PropertyChangedEventHandler? PropertyChanged; 13 | 14 | public string? Name 15 | { 16 | get 17 | { 18 | this.ThrowIfDisposed(); 19 | return this.name; 20 | } 21 | 22 | set 23 | { 24 | this.ThrowIfDisposed(); 25 | if (value == this.name) 26 | { 27 | return; 28 | } 29 | 30 | this.name = value; 31 | this.OnPropertyChanged(); 32 | } 33 | } 34 | 35 | public void Dispose() 36 | { 37 | if (this.disposed) 38 | { 39 | return; 40 | } 41 | 42 | this.disposed = true; 43 | } 44 | 45 | private void OnPropertyChanged([CallerMemberName] string? propertyName = null) 46 | { 47 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 48 | } 49 | 50 | private void ThrowIfDisposed() 51 | { 52 | if (this.disposed) 53 | { 54 | throw new ObjectDisposedException(typeof(DisposableFoo).FullName); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ValidCode/DontRequireNotificationFor.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode; 3 | 4 | public class DontRequireNotificationFor 5 | { 6 | private int setOnly; 7 | 8 | public DontRequireNotificationFor(int only) 9 | { 10 | this.GetOnly = only; 11 | this.setOnly = only; 12 | this.PublicGetPrivateSet = only; 13 | } 14 | 15 | public int GetOnly { get; } 16 | 17 | public int SetOnly 18 | { 19 | set => this.setOnly = value; 20 | } 21 | 22 | /// 23 | /// Assigned in ctor only is ok. 24 | /// 25 | public int PublicGetPrivateSet { get; private set; } 26 | 27 | public int ExpressionBody => 1; 28 | } 29 | -------------------------------------------------------------------------------- /ValidCode/EmptyTrySet.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode; 3 | 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public class EmptyTrySet : INotifyPropertyChanged 9 | { 10 | public event PropertyChangedEventHandler? PropertyChanged; 11 | 12 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 13 | { 14 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 15 | } 16 | 17 | protected bool TrySet(ref T field, T value, [CallerMemberName] string? propertyName = null) 18 | { 19 | if (EqualityComparer.Default.Equals(field, value)) 20 | { 21 | field = value; 22 | return false; 23 | } 24 | 25 | field = value; 26 | this.OnPropertyChanged(propertyName); 27 | return true; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ValidCode/Ignores/Abstract.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Ignores; 3 | 4 | public abstract class Abstract 5 | { 6 | public abstract int P { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /ValidCode/Ignores/Enumerator.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Ignores; 3 | 4 | using System.Collections; 5 | 6 | public class Enumerator : IEnumerator 7 | { 8 | public object? Current { get; private set; } 9 | 10 | public bool MoveNext() 11 | { 12 | switch (Current) 13 | { 14 | case int i 15 | when i < 5: 16 | Current = i + 1; 17 | return true; 18 | case null: 19 | Current = 0; 20 | return true; 21 | default: 22 | return false; 23 | } 24 | } 25 | 26 | public void Reset() 27 | { 28 | Current = null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ValidCode/Ignores/FooControl.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Ignores; 3 | 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | 7 | public class FooControl : Control 8 | { 9 | public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( 10 | nameof(Value), 11 | typeof(int), 12 | typeof(FooControl), 13 | new PropertyMetadata(default(int))); 14 | 15 | public int Value 16 | { 17 | get => (int)this.GetValue(ValueProperty); 18 | set => this.SetValue(ValueProperty, value); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ValidCode/Ignores/IgnoredProperties.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Ignores; 3 | 4 | public class IgnoredProperties 5 | { 6 | public IgnoredProperties(int privateSetAssignedInConstructor) 7 | { 8 | this.PrivateSetAssignedInConstructor = privateSetAssignedInConstructor; 9 | } 10 | 11 | public static int P { get; set; } 12 | 13 | public int GetOnly { get; } = 1; 14 | 15 | public int ExpressionBody => 1; 16 | 17 | public int ExpressionBodyGetter 18 | { 19 | get => 1; 20 | } 21 | 22 | public int StatementBodyGetter 23 | { 24 | #pragma warning disable INPC020 // Prefer expression body accessor. 25 | get { return 1; } 26 | #pragma warning restore INPC020 27 | } 28 | 29 | public int PrivateSetAssignedInConstructor { get; private set; } 30 | } 31 | -------------------------------------------------------------------------------- /ValidCode/Ignores/IntEnumerator.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Ignores; 3 | 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class IntEnumerator : IEnumerator 8 | { 9 | object IEnumerator.Current => Current; 10 | 11 | public int Current { get; private set; } 12 | 13 | public bool MoveNext() 14 | { 15 | switch (this.Current) 16 | { 17 | case int i 18 | when i < 5: 19 | this.Current = i + 1; 20 | return true; 21 | case -1: 22 | this.Current = 0; 23 | return true; 24 | default: 25 | return false; 26 | } 27 | } 28 | 29 | public void Reset() 30 | { 31 | this.Current = -1; 32 | } 33 | 34 | public void Dispose() 35 | { 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ValidCode/Ignores/SomeStream.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Ignores; 3 | 4 | using System.IO; 5 | 6 | public class SomeStream : Stream 7 | { 8 | public override bool CanRead { get; } 9 | 10 | public override bool CanSeek { get; } 11 | 12 | public override bool CanWrite { get; } 13 | 14 | public override long Length { get; } 15 | 16 | public override long Position { get; set; } 17 | 18 | public override void Flush() 19 | { 20 | throw new System.NotImplementedException(); 21 | } 22 | 23 | public override long Seek(long offset, SeekOrigin origin) 24 | { 25 | throw new System.NotImplementedException(); 26 | } 27 | 28 | public override void SetLength(long value) 29 | { 30 | throw new System.NotImplementedException(); 31 | } 32 | 33 | public override int Read(byte[] buffer, int offset, int count) 34 | { 35 | throw new System.NotImplementedException(); 36 | } 37 | 38 | public override void Write(byte[] buffer, int offset, int count) 39 | { 40 | throw new System.NotImplementedException(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ValidCode/Ignores/Struct.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Ignores; 3 | 4 | public struct Struct 5 | { 6 | public int P { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /ValidCode/Inheritance/ExpressionBodies.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Inheritance; 3 | 4 | public sealed class ExpressionBodies : ExpressionBodiesViewModelBase 5 | { 6 | private string? name; 7 | 8 | public string Greeting => $"Hello {this.name}"; 9 | 10 | public string? Name 11 | { 12 | get => this.name; 13 | 14 | set 15 | { 16 | if (value == this.name) 17 | { 18 | return; 19 | } 20 | 21 | this.name = value; 22 | this.OnPropertyChanged(); 23 | this.OnPropertyChanged(nameof(this.Greeting)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ValidCode/Inheritance/ExpressionBodiesViewModelBase.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Inheritance; 3 | 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public abstract class ExpressionBodiesViewModelBase : INotifyPropertyChanged 8 | { 9 | public event PropertyChangedEventHandler? PropertyChanged; 10 | 11 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 12 | } 13 | -------------------------------------------------------------------------------- /ValidCode/Inheritance/StatementBodies.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | #pragma warning disable INPC020 // Prefer expression body accessor. 3 | namespace ValidCode.Inheritance; 4 | 5 | public sealed class StatementBodies : StatementBodiesViewModelBase 6 | { 7 | private string? name; 8 | 9 | public string Greeting 10 | { 11 | get 12 | { 13 | return $"Hello {this.name}"; 14 | } 15 | } 16 | 17 | public string? Name 18 | { 19 | get 20 | { 21 | return this.name; 22 | } 23 | 24 | set 25 | { 26 | if (value == this.name) 27 | { 28 | return; 29 | } 30 | 31 | this.name = value; 32 | this.OnPropertyChanged(); 33 | this.OnPropertyChanged(nameof(this.Greeting)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ValidCode/Inheritance/StatementBodiesViewModelBase.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Inheritance; 3 | 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public abstract class StatementBodiesViewModelBase : INotifyPropertyChanged 8 | { 9 | public event PropertyChangedEventHandler? PropertyChanged; 10 | 11 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 12 | { 13 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ValidCode/Inheritance/UnderscoreNames.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Inheritance; 3 | 4 | public sealed class UnderscoreNames : UnderscoreNamesViewModelBase 5 | { 6 | private string? _name; 7 | 8 | public string Greeting => $"Hello {_name}"; 9 | 10 | public string? Name 11 | { 12 | get => _name; 13 | 14 | set 15 | { 16 | if (value == _name) 17 | { 18 | return; 19 | } 20 | 21 | _name = value; 22 | OnPropertyChanged(); 23 | OnPropertyChanged(nameof(Greeting)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ValidCode/Inheritance/UnderscoreNamesViewModelBase.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Inheritance; 3 | 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public abstract class UnderscoreNamesViewModelBase : INotifyPropertyChanged 8 | { 9 | public event PropertyChangedEventHandler? PropertyChanged; 10 | 11 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 12 | { 13 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ValidCode/InheritanceTrySet/ExpressionBodies.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.InheritanceTrySet; 3 | 4 | public sealed class ExpressionBodies : ExpressionBodiesViewModelBase 5 | { 6 | private string? name; 7 | private int value; 8 | 9 | public string Greeting => $"Hello {this.name}"; 10 | 11 | public string? Name 12 | { 13 | get => this.name; 14 | 15 | set 16 | { 17 | if (this.TrySet(ref this.name, value)) 18 | { 19 | this.OnPropertyChanged(nameof(this.Greeting)); 20 | } 21 | } 22 | } 23 | 24 | public int Value 25 | { 26 | get => this.value; 27 | set => this.TrySet(ref this.value, value); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ValidCode/InheritanceTrySet/ExpressionBodiesViewModelBase.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.InheritanceTrySet; 3 | 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public abstract class ExpressionBodiesViewModelBase : INotifyPropertyChanged 9 | { 10 | public event PropertyChangedEventHandler? PropertyChanged; 11 | 12 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 13 | 14 | protected bool TrySet(ref T field, T value, [CallerMemberName] string? propertyName = null) 15 | { 16 | if (EqualityComparer.Default.Equals(field, value)) 17 | { 18 | return false; 19 | } 20 | 21 | field = value; 22 | this.OnPropertyChanged(propertyName); 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ValidCode/InheritanceTrySet/StatementBodies.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | #pragma warning disable INPC020 // Prefer expression body accessor. 3 | namespace ValidCode.InheritanceTrySet; 4 | 5 | public sealed class StatementBodies : StatementBodiesViewModelBase 6 | { 7 | private string? name; 8 | private int value; 9 | 10 | public string Greeting => $"Hello {this.name}"; 11 | 12 | public string? Name 13 | { 14 | get => this.name; 15 | 16 | set 17 | { 18 | if (this.TrySet(ref this.name, value)) 19 | { 20 | this.OnPropertyChanged(nameof(this.Greeting)); 21 | } 22 | } 23 | } 24 | 25 | public int Value 26 | { 27 | get 28 | { 29 | return this.value; 30 | } 31 | 32 | set 33 | { 34 | this.TrySet(ref this.value, value); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ValidCode/InheritanceTrySet/StatementBodiesViewModelBase.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.InheritanceTrySet; 3 | 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public abstract class StatementBodiesViewModelBase : INotifyPropertyChanged 9 | { 10 | public event PropertyChangedEventHandler? PropertyChanged; 11 | 12 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 13 | { 14 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 15 | } 16 | 17 | protected bool TrySet(ref T field, T value, [CallerMemberName] string? propertyName = null) 18 | { 19 | if (EqualityComparer.Default.Equals(field, value)) 20 | { 21 | return false; 22 | } 23 | 24 | field = value; 25 | this.OnPropertyChanged(propertyName); 26 | return true; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ValidCode/InheritanceTrySet/UnderscoreNames.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.InheritanceTrySet; 3 | 4 | public sealed class UnderscoreNames : UnderscoreNamesViewModelBase 5 | { 6 | private string? _name; 7 | private int _value; 8 | 9 | public string Greeting => $"Hello {_name}"; 10 | 11 | public string? Name 12 | { 13 | get => _name; 14 | 15 | set 16 | { 17 | if (TrySet(ref _name, value)) 18 | { 19 | OnPropertyChanged(nameof(Greeting)); 20 | } 21 | } 22 | } 23 | 24 | public int Value 25 | { 26 | get => _value; 27 | set => TrySet(ref _value, value); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ValidCode/InheritanceTrySet/UnderscoreNamesViewModelBase.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.InheritanceTrySet; 3 | 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public abstract class UnderscoreNamesViewModelBase : INotifyPropertyChanged 9 | { 10 | public event PropertyChangedEventHandler? PropertyChanged; 11 | 12 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 13 | { 14 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 15 | } 16 | 17 | protected bool TrySet(ref T field, T value, [CallerMemberName] string? propertyName = null) 18 | { 19 | if (EqualityComparer.Default.Equals(field, value)) 20 | { 21 | return false; 22 | } 23 | 24 | field = value; 25 | OnPropertyChanged(propertyName); 26 | return true; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ValidCode/IntAndStringProperty.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode; 3 | 4 | using System.ComponentModel; 5 | using System.Globalization; 6 | using System.Runtime.CompilerServices; 7 | 8 | public class IntAndStringProperty : INotifyPropertyChanged 9 | { 10 | private int p1; 11 | 12 | public event PropertyChangedEventHandler? PropertyChanged; 13 | 14 | public int P1 15 | { 16 | get => this.p1; 17 | set 18 | { 19 | if (value == this.p1) 20 | { 21 | return; 22 | } 23 | 24 | this.p1 = value; 25 | this.OnPropertyChanged(); 26 | this.OnPropertyChanged(nameof(this.P2)); 27 | } 28 | } 29 | 30 | public string P2 31 | { 32 | get => this.p1.ToString(CultureInfo.InvariantCulture); 33 | set => this.P1 = int.Parse(value, CultureInfo.InvariantCulture); 34 | } 35 | 36 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 37 | { 38 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ValidCode/Interfaces/Generic.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Interfaces; 3 | 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public class Generic : IValue, INotifyPropertyChanged 8 | { 9 | #pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. 10 | private T value; 11 | #pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. 12 | 13 | public event PropertyChangedEventHandler? PropertyChanged; 14 | 15 | public T Value 16 | { 17 | get => this.value; 18 | set 19 | { 20 | if (System.Collections.Generic.EqualityComparer.Default.Equals(value, this.value)) 21 | { 22 | return; 23 | } 24 | 25 | this.value = value; 26 | this.OnPropertyChanged(); 27 | } 28 | } 29 | 30 | object? IValue.Value 31 | { 32 | get => this.value; 33 | #pragma warning disable CS8601 // Possible null reference assignment. 34 | set => this.Value = (T?)value; 35 | #pragma warning restore CS8601 // Possible null reference assignment. 36 | } 37 | 38 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 39 | { 40 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ValidCode/Interfaces/GenericAutoProperty.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Interfaces; 3 | 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public class GenericAutoProperty : IValue, INotifyPropertyChanged 8 | { 9 | #pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. 10 | private T value; 11 | #pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. 12 | 13 | public event PropertyChangedEventHandler? PropertyChanged; 14 | 15 | public T Value 16 | { 17 | get => this.value; 18 | set 19 | { 20 | if (System.Collections.Generic.EqualityComparer.Default.Equals(value, this.value)) 21 | { 22 | return; 23 | } 24 | 25 | this.value = value; 26 | this.OnPropertyChanged(); 27 | } 28 | } 29 | 30 | object? IValue.Value 31 | { 32 | get => this.Value; 33 | #pragma warning disable CS8601 // Possible null reference assignment. 34 | set => this.Value = (T?)value; 35 | #pragma warning restore CS8601 // Possible null reference assignment. 36 | } 37 | 38 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 39 | { 40 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ValidCode/Interfaces/GenericClass.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Interfaces; 3 | 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public class GenericClass : IValue, INotifyPropertyChanged 8 | { 9 | #pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. 10 | private T value; 11 | #pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. 12 | 13 | public event PropertyChangedEventHandler? PropertyChanged; 14 | 15 | public T Value 16 | { 17 | get => this.value; 18 | set 19 | { 20 | if (System.Collections.Generic.EqualityComparer.Default.Equals(value, this.value)) 21 | { 22 | return; 23 | } 24 | 25 | this.value = value; 26 | this.OnPropertyChanged(); 27 | } 28 | } 29 | 30 | object? IValue.Value 31 | { 32 | get => this.value; 33 | #pragma warning disable CS8601 // Possible null reference assignment. 34 | set => this.Value = (T?)value; 35 | #pragma warning restore CS8601 // Possible null reference assignment. 36 | } 37 | 38 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 39 | { 40 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ValidCode/Interfaces/IValue.cs: -------------------------------------------------------------------------------- 1 | namespace ValidCode.Interfaces; 2 | 3 | interface IValue 4 | { 5 | object? Value { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /ValidCode/Interfaces/WithString.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Interfaces; 3 | 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public class WithString : IValue, INotifyPropertyChanged 8 | { 9 | private string? value; 10 | 11 | public event PropertyChangedEventHandler? PropertyChanged; 12 | 13 | public string? Value 14 | { 15 | get => this.value; 16 | set 17 | { 18 | if (value == this.value) 19 | { 20 | return; 21 | } 22 | 23 | this.value = value; 24 | this.OnPropertyChanged(); 25 | } 26 | } 27 | 28 | object? IValue.Value 29 | { 30 | get => this.value; 31 | set => this.Value = (string?)value; 32 | } 33 | 34 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 35 | { 36 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ValidCode/Interfaces/WithStringAutoProperty.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Interfaces; 3 | 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public class WithStringAutoProperty : IValue, INotifyPropertyChanged 8 | { 9 | private string? value; 10 | 11 | public event PropertyChangedEventHandler? PropertyChanged; 12 | 13 | public string? Value 14 | { 15 | get => this.value; 16 | set 17 | { 18 | if (value == this.value) 19 | { 20 | return; 21 | } 22 | 23 | this.value = value; 24 | this.OnPropertyChanged(); 25 | } 26 | } 27 | 28 | object? IValue.Value 29 | { 30 | get => this.Value; 31 | set => this.Value = (string?)value; 32 | } 33 | 34 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 35 | { 36 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ValidCode/LockInSetter.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode; 3 | 4 | using System; 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public class LockInSetter : INotifyPropertyChanged 9 | { 10 | private readonly object _busyLock = new(); 11 | private bool _value; 12 | 13 | public event PropertyChangedEventHandler? PropertyChanged; 14 | 15 | public bool Value 16 | { 17 | get => this._value; 18 | private set 19 | { 20 | lock (this._busyLock) 21 | { 22 | if (value && this._value) 23 | { 24 | throw new InvalidOperationException(); 25 | } 26 | 27 | if (value == this._value) 28 | { 29 | return; 30 | } 31 | 32 | this._value = value; 33 | } 34 | 35 | this.OnPropertyChanged(); 36 | } 37 | } 38 | 39 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 40 | { 41 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ValidCode/Mouse.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode; 3 | 4 | using System; 5 | using System.ComponentModel; 6 | using System.Drawing; 7 | using System.Runtime.InteropServices; 8 | using System.Threading; 9 | 10 | public static class Mouse 11 | { 12 | public static Point Position 13 | { 14 | get 15 | { 16 | if (GetCursorPos(out var p)) 17 | { 18 | return p; 19 | } 20 | 21 | throw new Win32Exception(); 22 | } 23 | 24 | set 25 | { 26 | if (!SetCursorPos(value.X, value.Y)) 27 | { 28 | throw new Win32Exception(); 29 | } 30 | 31 | Thread.Sleep(TimeSpan.FromMilliseconds(10)); 32 | } 33 | } 34 | 35 | [DllImport("user32.dll", SetLastError = true)] 36 | private static extern bool GetCursorPos(out Point lpPoint); 37 | 38 | [DllImport("user32.dll", SetLastError = true)] 39 | private static extern bool SetCursorPos(int x, int y); 40 | } 41 | -------------------------------------------------------------------------------- /ValidCode/Recursion/ExpressionBodies.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | #pragma warning disable INPC007, INPC015, CS0067 3 | namespace ValidCode.Recursion; 4 | 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public sealed class ExpressionBodies : INotifyPropertyChanged 9 | { 10 | private string? name; 11 | private int value; 12 | 13 | public event PropertyChangedEventHandler? PropertyChanged; 14 | 15 | public string Greeting => this.Greeting; 16 | 17 | public string? Name 18 | { 19 | get => this.Name; 20 | 21 | set 22 | { 23 | if (this.TrySet(ref this.name, value)) 24 | { 25 | this.OnPropertyChanged(nameof(this.Greeting)); 26 | } 27 | } 28 | } 29 | 30 | public int Value 31 | { 32 | get => this.Value; 33 | set => this.TrySet(ref this.value, value); 34 | } 35 | 36 | private void OnPropertyChanged([CallerMemberName] string? propertyName = null) => this.OnPropertyChanged(propertyName); 37 | 38 | private bool TrySet(ref T field, T value, [CallerMemberName] string? propertyName = null) => this.TrySet(ref field, value, propertyName); 39 | } 40 | -------------------------------------------------------------------------------- /ValidCode/Recursion/NotRecursion.cs: -------------------------------------------------------------------------------- 1 | namespace ValidCode.Recursion; 2 | 3 | using System; 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public class NotRecursion : INotifyPropertyChanged 8 | { 9 | public event PropertyChangedEventHandler? PropertyChanged; 10 | 11 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 12 | { 13 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 14 | } 15 | 16 | protected bool Set(ref T location, T value, [CallerMemberName] string? propertyName = null) 17 | { 18 | if (RuntimeHelpers.Equals(location, value)) return false; 19 | location = value; 20 | OnPropertyChanged(propertyName); 21 | return true; 22 | } 23 | 24 | record DifferentClass 25 | { 26 | public int P; 27 | } 28 | 29 | private int p; 30 | public int P 31 | { 32 | get => p; 33 | set 34 | { 35 | if (!Set(ref p, value)) return; 36 | 37 | _ = new DifferentClass { P = value }; 38 | _ = new DifferentClass() with { P = value }; 39 | 40 | { 41 | int P; 42 | P = value; 43 | } 44 | 45 | _ = new Action(P => 46 | { 47 | P = value; 48 | }); 49 | 50 | LocalFunction(42); 51 | void LocalFunction(int P) 52 | { 53 | P = value; 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ValidCode/Recursion/StatementBodies.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | #pragma warning disable INPC007, INPC015, INPC020, CS0067 3 | namespace ValidCode.Recursion; 4 | 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public sealed class StatementnBodies : INotifyPropertyChanged 9 | { 10 | private string? name; 11 | private int value; 12 | 13 | public event PropertyChangedEventHandler? PropertyChanged; 14 | 15 | public string Greeting1 16 | { 17 | get 18 | { 19 | return this.Greeting1; 20 | } 21 | } 22 | 23 | public string Greeting2 24 | { 25 | get 26 | { 27 | return $"{this.Name}"; 28 | } 29 | } 30 | 31 | public string? Name 32 | { 33 | get 34 | { 35 | return this.Name; 36 | } 37 | 38 | set 39 | { 40 | if (this.TrySet(ref this.name, value)) 41 | { 42 | this.OnPropertyChanged(nameof(this.Greeting1)); 43 | this.OnPropertyChanged(nameof(this.Greeting2)); 44 | } 45 | } 46 | } 47 | 48 | public int Value 49 | { 50 | get 51 | { 52 | return this.Value; 53 | } 54 | 55 | set 56 | { 57 | this.TrySet(ref this.value, value); 58 | } 59 | } 60 | 61 | private void OnPropertyChanged([CallerMemberName] string? propertyName = null) 62 | { 63 | this.OnPropertyChanged(propertyName); 64 | } 65 | 66 | private bool TrySet(ref T field, T value, [CallerMemberName] string? propertyName = null) 67 | { 68 | return this.TrySet(ref field, value, propertyName); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ValidCode/RelayProperty.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode; 3 | 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public class RelayProperty : INotifyPropertyChanged 8 | { 9 | private int p1; 10 | 11 | public event PropertyChangedEventHandler? PropertyChanged; 12 | 13 | public int P1 14 | { 15 | get => this.p1; 16 | set 17 | { 18 | if (value == this.p1) 19 | { 20 | return; 21 | } 22 | 23 | this.p1 = value; 24 | this.OnPropertyChanged(); 25 | this.OnPropertyChanged(nameof(this.P2)); 26 | this.OnPropertyChanged(nameof(this.P3)); 27 | } 28 | } 29 | 30 | public int P2 31 | { 32 | get => this.P1; 33 | set => this.P1 = value; 34 | } 35 | 36 | public int P3 37 | { 38 | #pragma warning disable INPC017 // INPC017 Backing field name must match. 39 | get => this.p1; 40 | #pragma warning restore INPC017 41 | set => this.P1 = value; 42 | } 43 | 44 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 45 | { 46 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ValidCode/StaticClass.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode; 3 | 4 | using System.ComponentModel; 5 | 6 | public static class StaticClass 7 | { 8 | private static string? name; 9 | private static int number; 10 | 11 | public static event PropertyChangedEventHandler? PropertyChanged; 12 | 13 | public static string? Name 14 | { 15 | get => name; 16 | set 17 | { 18 | if (name == value) 19 | { 20 | return; 21 | } 22 | 23 | name = value; 24 | PropertyChanged?.Invoke(null, new PropertyChangedEventArgs(nameof(Name))); 25 | } 26 | } 27 | 28 | public static int Number 29 | { 30 | get => number; 31 | set 32 | { 33 | if(value == number) 34 | { 35 | return; 36 | } 37 | 38 | number = value; 39 | OnPropertyChanged(); 40 | } 41 | } 42 | 43 | private static void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null) 44 | { 45 | PropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName)); 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /ValidCode/TrySet/ExpressionBodies.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.TrySet; 3 | 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public sealed class ExpressionBodies : INotifyPropertyChanged 9 | { 10 | private string? name; 11 | private int value; 12 | 13 | public event PropertyChangedEventHandler? PropertyChanged; 14 | 15 | public string Greeting => $"Hello {this.name}"; 16 | 17 | public string? Name 18 | { 19 | get => this.name; 20 | 21 | set 22 | { 23 | if (this.TrySet(ref this.name, value)) 24 | { 25 | this.OnPropertyChanged(nameof(this.Greeting)); 26 | } 27 | } 28 | } 29 | 30 | public int Value 31 | { 32 | get => this.value; 33 | set => this.TrySet(ref this.value, value); 34 | } 35 | 36 | private void OnPropertyChanged([CallerMemberName] string? propertyName = null) => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 37 | 38 | private bool TrySet(ref T field, T value, [CallerMemberName] string? propertyName = null) 39 | { 40 | if (EqualityComparer.Default.Equals(field, value)) 41 | { 42 | return false; 43 | } 44 | 45 | field = value; 46 | this.OnPropertyChanged(propertyName); 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ValidCode/TrySet/StatementBodies.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | #pragma warning disable INPC020 // Prefer expression body accessor. 3 | namespace ValidCode.TrySet; 4 | 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Runtime.CompilerServices; 8 | 9 | public sealed class StatementnBodies : INotifyPropertyChanged 10 | { 11 | private string? name; 12 | private int value; 13 | 14 | public event PropertyChangedEventHandler? PropertyChanged; 15 | 16 | public string Greeting 17 | { 18 | get 19 | { 20 | return $"Hello {this.name}"; 21 | } 22 | } 23 | 24 | public string? Name 25 | { 26 | get 27 | { 28 | return this.name; 29 | } 30 | 31 | set 32 | { 33 | if (this.TrySet(ref this.name, value)) 34 | { 35 | this.OnPropertyChanged(nameof(this.Greeting)); 36 | } 37 | } 38 | } 39 | 40 | public int Value 41 | { 42 | get 43 | { 44 | return this.value; 45 | } 46 | 47 | set 48 | { 49 | this.TrySet(ref this.value, value); 50 | } 51 | } 52 | 53 | private void OnPropertyChanged([CallerMemberName] string? propertyName = null) 54 | { 55 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 56 | } 57 | 58 | private bool TrySet(ref T field, T value, [CallerMemberName] string? propertyName = null) 59 | { 60 | if (EqualityComparer.Default.Equals(field, value)) 61 | { 62 | return false; 63 | } 64 | 65 | field = value; 66 | this.OnPropertyChanged(propertyName); 67 | return true; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ValidCode/TrySet/UnderscoreNames.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.TrySet; 3 | 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public sealed class UnderscoreNames : INotifyPropertyChanged 9 | { 10 | private string? _name; 11 | private int _value; 12 | 13 | public event PropertyChangedEventHandler? PropertyChanged; 14 | 15 | public string Greeting => $"Hello {_name}"; 16 | 17 | public string? Name 18 | { 19 | get => _name; 20 | 21 | set 22 | { 23 | if (TrySet(ref _name, value)) 24 | { 25 | OnPropertyChanged(nameof(Greeting)); 26 | } 27 | } 28 | } 29 | 30 | public int Value 31 | { 32 | get => _value; 33 | set => TrySet(ref _value, value); 34 | } 35 | 36 | private void OnPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 37 | 38 | private bool TrySet(ref T field, T value, [CallerMemberName] string? propertyName = null) 39 | { 40 | if (EqualityComparer.Default.Equals(field, value)) 41 | { 42 | return false; 43 | } 44 | 45 | field = value; 46 | OnPropertyChanged(propertyName); 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ValidCode/ValidCode.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0-windows 5 | true 6 | latest 7 | Enable 8 | NU1701 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /ValidCode/Vanilla/ExpressionBodies.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Vanilla; 3 | 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public sealed class ExpressionBodies : INotifyPropertyChanged 8 | { 9 | private string? name; 10 | 11 | public event PropertyChangedEventHandler? PropertyChanged; 12 | 13 | public string Greeting => $"Hello {this.name}"; 14 | 15 | public string? Name 16 | { 17 | get => this.name; 18 | 19 | set 20 | { 21 | if (value == this.name) 22 | { 23 | return; 24 | } 25 | 26 | this.name = value; 27 | this.OnPropertyChanged(); 28 | this.OnPropertyChanged(nameof(this.Greeting)); 29 | } 30 | } 31 | 32 | private void OnPropertyChanged([CallerMemberName] string? propertyName = null) => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 33 | } 34 | -------------------------------------------------------------------------------- /ValidCode/Vanilla/StatementBodies.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | #pragma warning disable INPC020 // Prefer expression body accessor. 3 | namespace ValidCode.Vanilla; 4 | 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public sealed class StatementBodies : INotifyPropertyChanged 9 | { 10 | private string? name; 11 | 12 | public event PropertyChangedEventHandler? PropertyChanged; 13 | 14 | public string Greeting 15 | { 16 | get 17 | { 18 | return $"Hello {this.name}"; 19 | } 20 | } 21 | 22 | public string? Name 23 | { 24 | get 25 | { 26 | return this.name; 27 | } 28 | 29 | set 30 | { 31 | if (value == this.name) 32 | { 33 | return; 34 | } 35 | 36 | this.name = value; 37 | this.OnPropertyChanged(); 38 | this.OnPropertyChanged(nameof(this.Greeting)); 39 | } 40 | } 41 | 42 | private void OnPropertyChanged([CallerMemberName] string? propertyName = null) 43 | { 44 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ValidCode/Vanilla/UnderscoreNames.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode.Vanilla; 3 | 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public sealed class UnderscoreNames : INotifyPropertyChanged 8 | { 9 | private string? _name; 10 | 11 | public event PropertyChangedEventHandler? PropertyChanged; 12 | 13 | public string Greeting => $"Hello {_name}"; 14 | 15 | public string? Name 16 | { 17 | get => _name; 18 | 19 | set 20 | { 21 | if (value == _name) 22 | { 23 | return; 24 | } 25 | 26 | _name = value; 27 | OnPropertyChanged(); 28 | OnPropertyChanged(nameof(Greeting)); 29 | } 30 | } 31 | 32 | private void OnPropertyChanged([CallerMemberName] string? propertyName = null) 33 | { 34 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ValidCode/WithEventDeclaration.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | #pragma warning disable IDE1006 // Naming Styles 3 | namespace ValidCode; 4 | 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public sealed class WithEventDeclaration : INotifyPropertyChanged 9 | { 10 | private string? name; 11 | 12 | public event PropertyChangedEventHandler? PropertyChanged 13 | { 14 | add => this.propertyChanged += value; 15 | remove => this.propertyChanged -= value; 16 | } 17 | 18 | private event PropertyChangedEventHandler? propertyChanged; 19 | 20 | public string? Name 21 | { 22 | get => this.name; 23 | 24 | set 25 | { 26 | if (value == this.name) 27 | { 28 | return; 29 | } 30 | 31 | this.name = value; 32 | this.OnPropertyChanged(); 33 | } 34 | } 35 | 36 | private void OnPropertyChanged([CallerMemberName] string? propertyName = null) 37 | { 38 | this.propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ValidCode/WithEventDeclarationNullableDisabled.cs: -------------------------------------------------------------------------------- 1 | #nullable disable 2 | namespace ValidCode; 3 | 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public sealed class WithEventDeclarationNullableDisabled : INotifyPropertyChanged 8 | { 9 | private string name; 10 | 11 | public event PropertyChangedEventHandler PropertyChanged 12 | { 13 | add => this.propertyChanged += value; 14 | remove => this.propertyChanged -= value; 15 | } 16 | 17 | private event PropertyChangedEventHandler propertyChanged; 18 | 19 | public string Name 20 | { 21 | get => this.name; 22 | 23 | set 24 | { 25 | if (value == this.name) 26 | { 27 | return; 28 | } 29 | 30 | this.name = value; 31 | this.OnPropertyChanged(); 32 | } 33 | } 34 | 35 | private void OnPropertyChanged([CallerMemberName] string propertyName = null) 36 | { 37 | this.propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ValidCode/WithMultidimensional.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode; 3 | 4 | public class WithMultidimensional 5 | { 6 | public WithMultidimensional() 7 | { 8 | this.Data2D = new int[3, 3]; 9 | for (var i = 0; i < 3; ++i) 10 | { 11 | for (var j = 0; j < 3; ++j) 12 | { 13 | this.Data2D[i, j] = i * j; 14 | } 15 | } 16 | } 17 | 18 | public int[,] Data2D { get; } 19 | } 20 | -------------------------------------------------------------------------------- /ValidCode/WithSpeeds.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode; 3 | 4 | using System; 5 | using System.ComponentModel; 6 | using System.Runtime.CompilerServices; 7 | 8 | public class WithSpeeds : INotifyPropertyChanged 9 | { 10 | private double speed; 11 | 12 | public event PropertyChangedEventHandler? PropertyChanged; 13 | 14 | public bool IsSpeed1 15 | { 16 | get => Math.Abs(this.speed - 1) < 1E-2; 17 | set => this.Speed = 1; 18 | } 19 | 20 | public bool IsSpeed2 21 | { 22 | get => Math.Abs(this.speed - 2) < 1E-2; 23 | set => this.Speed = 2; 24 | } 25 | 26 | public double Speed 27 | { 28 | get => this.speed; 29 | 30 | set 31 | { 32 | if (value.Equals(this.speed)) 33 | { 34 | return; 35 | } 36 | 37 | this.speed = value; 38 | this.OnPropertyChanged(); 39 | this.OnPropertyChanged(nameof(this.IsSpeed1)); 40 | this.OnPropertyChanged(nameof(this.IsSpeed2)); 41 | } 42 | } 43 | 44 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 45 | { 46 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ValidCode/WithValidation.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable All 2 | namespace ValidCode; 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Runtime.CompilerServices; 8 | 9 | public class WithValidation : INotifyPropertyChanged 10 | { 11 | private int p1; 12 | private int p2; 13 | 14 | public WithValidation(int p1, int p2) 15 | { 16 | this.P1 = p1; 17 | this.P2 = p2; 18 | } 19 | 20 | public event PropertyChangedEventHandler? PropertyChanged; 21 | 22 | public int P1 23 | { 24 | get => this.p1; 25 | set 26 | { 27 | if (value < 0) 28 | { 29 | throw new ArgumentException("Expected greater or equal to zero."); 30 | } 31 | 32 | if (value == this.p1) 33 | { 34 | return; 35 | } 36 | 37 | this.p1 = value; 38 | this.OnPropertyChanged(); 39 | } 40 | } 41 | 42 | public int P2 43 | { 44 | get => this.p2; 45 | set 46 | { 47 | if (value < this.p1) 48 | { 49 | throw new ArgumentException("Expected greater or equal to zero."); 50 | } 51 | 52 | this.TrySet(ref this.p2, value); 53 | } 54 | } 55 | 56 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 57 | { 58 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 59 | } 60 | 61 | protected bool TrySet(ref T field, T value, [CallerMemberName] string? propertyName = null) 62 | { 63 | if (EqualityComparer.Default.Equals(field, value)) 64 | { 65 | return false; 66 | } 67 | 68 | field = value; 69 | this.OnPropertyChanged(propertyName); 70 | return true; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /ValidCode/Wrapping/WithFields.cs: -------------------------------------------------------------------------------- 1 | namespace ValidCode.Wrapping; 2 | 3 | public class WithFields 4 | { 5 | public int F1; 6 | public int F2; 7 | } 8 | -------------------------------------------------------------------------------- /ValidCode/Wrapping/WrappingFields.cs: -------------------------------------------------------------------------------- 1 | namespace ValidCode.Wrapping; 2 | 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public class WrappingFields : INotifyPropertyChanged 8 | { 9 | private readonly WithFields withFields = new(); 10 | 11 | public event PropertyChangedEventHandler? PropertyChanged; 12 | 13 | public int P1 14 | { 15 | get => this.withFields.F1; 16 | set 17 | { 18 | if (value == this.withFields.F1) 19 | { 20 | return; 21 | } 22 | 23 | this.withFields.F1 = value; 24 | this.OnPropertyChanged(); 25 | } 26 | } 27 | 28 | public int P2 29 | { 30 | get => this.withFields.F2; 31 | set => this.TrySet(ref this.withFields.F2, value); 32 | } 33 | 34 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 35 | { 36 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 37 | } 38 | 39 | protected bool TrySet(ref T field, T value, [CallerMemberName] string? propertyName = null) 40 | { 41 | if (EqualityComparer.Default.Equals(field, value)) 42 | { 43 | return false; 44 | } 45 | 46 | field = value; 47 | this.OnPropertyChanged(propertyName); 48 | return true; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ValidCode/Wrapping/WrappingProperties.cs: -------------------------------------------------------------------------------- 1 | namespace ValidCode.Wrapping; 2 | 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | public class WrappingProperties : INotifyPropertyChanged 8 | { 9 | private WithProperties withProperties = new(); 10 | 11 | public event PropertyChangedEventHandler? PropertyChanged; 12 | 13 | public int P1 14 | { 15 | get => this.withProperties.P1; 16 | set 17 | { 18 | if (value == this.withProperties.P1) 19 | { 20 | return; 21 | } 22 | 23 | this.withProperties.P1 = value; 24 | this.OnPropertyChanged(); 25 | } 26 | } 27 | 28 | public int P2 29 | { 30 | get => this.withProperties.P2; 31 | #pragma warning disable INPC003 // Notify when property changes. 32 | set => this.TrySet(ref this.withProperties, new WithProperties { P1 = this.P1, P2 = value }); 33 | #pragma warning restore INPC003 // Notify when property changes. 34 | } 35 | 36 | protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) 37 | { 38 | this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 39 | } 40 | 41 | protected bool TrySet(ref T field, T value, [CallerMemberName] string? propertyName = null) 42 | { 43 | if (EqualityComparer.Default.Equals(field, value)) 44 | { 45 | return false; 46 | } 47 | 48 | field = value; 49 | this.OnPropertyChanged(propertyName); 50 | return true; 51 | } 52 | 53 | private class WithProperties 54 | { 55 | public int P1 { get; set; } 56 | 57 | public int P2 { get; set; } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2022 2 | 3 | before_build: 4 | - ps: dotnet restore 5 | 6 | configuration: Release 7 | build: 8 | verbosity: minimal 9 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | pool: 2 | vmImage: 'windows-2022' 3 | 4 | steps: 5 | 6 | - task: VSBuild@1 7 | displayName: build 8 | inputs: 9 | configuration: 'Release' 10 | msbuildArgs: '/restore' 11 | 12 | - task: DotNetCoreCLI@2 13 | displayName: test 14 | inputs: 15 | command: 'test' 16 | nobuild: true -------------------------------------------------------------------------------- /documentation/INPC001.md: -------------------------------------------------------------------------------- 1 | # INPC001 2 | ## The class has mutable properties and should implement INotifyPropertyChanged 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC001 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [ClassDeclarationAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/ClassDeclarationAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | The class has mutable properties and should implement INotifyPropertyChanged. 15 | The default severity is warning meaning there will be a squiggle and a build warning. 16 | Setting severity to `Hidden`turns it into a refactoring. Then only the code fix shows up on the class but no warning. 17 | ![ImplementInpc](https://user-images.githubusercontent.com/1640096/63153711-44091880-c00f-11e9-9185-564c52575b4a.gif) 18 | 19 | ## Motivation 20 | 21 | This nag is helpful for finding and fixing places where we have forgotten to implement `INotifyPropertyChanged` 22 | 23 | ## How to fix violations 24 | 25 | Use the code fix. 26 | 27 | 28 | ## Configure severity 29 | 30 | ### Via ruleset file. 31 | 32 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 33 | 34 | ### Via #pragma directive. 35 | ```C# 36 | #pragma warning disable INPC001 // The class has mutable properties and should implement INotifyPropertyChanged 37 | Code violating the rule here 38 | #pragma warning restore INPC001 // The class has mutable properties and should implement INotifyPropertyChanged 39 | ``` 40 | 41 | Or put this at the top of the file to disable all instances. 42 | ```C# 43 | #pragma warning disable INPC001 // The class has mutable properties and should implement INotifyPropertyChanged 44 | ``` 45 | 46 | ### Via attribute `[SuppressMessage]`. 47 | 48 | ```C# 49 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 50 | "INPC001:The class has mutable properties and should implement INotifyPropertyChanged", 51 | Justification = "Reason...")] 52 | ``` 53 | 54 | -------------------------------------------------------------------------------- /documentation/INPC004.md: -------------------------------------------------------------------------------- 1 | # INPC004 2 | ## Use [CallerMemberName] 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC004 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [ArgumentAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/ArgumentAnalyzer.cs) 11 | | | [MethodDeclarationAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/MethodDeclarationAnalyzer.cs) 12 | 13 | ## Description 14 | 15 | Use [CallerMemberName]. 16 | 17 | ## Motivation 18 | 19 | ADD MOTIVATION HERE 20 | 21 | ## How to fix violations 22 | 23 | ADD HOW TO FIX VIOLATIONS HERE 24 | 25 | 26 | ## Configure severity 27 | 28 | ### Via ruleset file. 29 | 30 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 31 | 32 | ### Via #pragma directive. 33 | ```C# 34 | #pragma warning disable INPC004 // Use [CallerMemberName] 35 | Code violating the rule here 36 | #pragma warning restore INPC004 // Use [CallerMemberName] 37 | ``` 38 | 39 | Or put this at the top of the file to disable all instances. 40 | ```C# 41 | #pragma warning disable INPC004 // Use [CallerMemberName] 42 | ``` 43 | 44 | ### Via attribute `[SuppressMessage]`. 45 | 46 | ```C# 47 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 48 | "INPC004:Use [CallerMemberName]", 49 | Justification = "Reason...")] 50 | ``` 51 | -------------------------------------------------------------------------------- /documentation/INPC005.md: -------------------------------------------------------------------------------- 1 | # INPC005 2 | ## Check if value is different before notifying 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC005 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [SetAccessorAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/SetAccessorAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | Check if value is different before notifying. 15 | 16 | ## Motivation 17 | 18 | ADD MOTIVATION HERE 19 | 20 | ## How to fix violations 21 | 22 | ADD HOW TO FIX VIOLATIONS HERE 23 | 24 | 25 | ## Configure severity 26 | 27 | ### Via ruleset file. 28 | 29 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 30 | 31 | ### Via #pragma directive. 32 | ```C# 33 | #pragma warning disable INPC005 // Check if value is different before notifying 34 | Code violating the rule here 35 | #pragma warning restore INPC005 // Check if value is different before notifying 36 | ``` 37 | 38 | Or put this at the top of the file to disable all instances. 39 | ```C# 40 | #pragma warning disable INPC005 // Check if value is different before notifying 41 | ``` 42 | 43 | ### Via attribute `[SuppressMessage]`. 44 | 45 | ```C# 46 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 47 | "INPC005:Check if value is different before notifying", 48 | Justification = "Reason...")] 49 | ``` 50 | -------------------------------------------------------------------------------- /documentation/INPC007.md: -------------------------------------------------------------------------------- 1 | # INPC007 2 | ## The class has PropertyChangedEvent but no invoker 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC007 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [EventAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/EventAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | The class has PropertyChangedEvent but no invoker. 15 | 16 | ## Motivation 17 | 18 | ADD MOTIVATION HERE 19 | 20 | ## How to fix violations 21 | 22 | ADD HOW TO FIX VIOLATIONS HERE 23 | 24 | 25 | ## Configure severity 26 | 27 | ### Via ruleset file. 28 | 29 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 30 | 31 | ### Via #pragma directive. 32 | ```C# 33 | #pragma warning disable INPC007 // The class has PropertyChangedEvent but no invoker 34 | Code violating the rule here 35 | #pragma warning restore INPC007 // The class has PropertyChangedEvent but no invoker 36 | ``` 37 | 38 | Or put this at the top of the file to disable all instances. 39 | ```C# 40 | #pragma warning disable INPC007 // The class has PropertyChangedEvent but no invoker 41 | ``` 42 | 43 | ### Via attribute `[SuppressMessage]`. 44 | 45 | ```C# 46 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 47 | "INPC007:The class has PropertyChangedEvent but no invoker", 48 | Justification = "Reason...")] 49 | ``` 50 | -------------------------------------------------------------------------------- /documentation/INPC008.md: -------------------------------------------------------------------------------- 1 | # INPC008 2 | ## Struct must not implement INotifyPropertyChanged 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC008 7 | | Severity | Error 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [StructAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/StructAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | Struct must not implement INotifyPropertyChanged. 15 | 16 | ## Motivation 17 | 18 | ADD MOTIVATION HERE 19 | 20 | ## How to fix violations 21 | 22 | ADD HOW TO FIX VIOLATIONS HERE 23 | 24 | 25 | ## Configure severity 26 | 27 | ### Via ruleset file. 28 | 29 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 30 | 31 | ### Via #pragma directive. 32 | ```C# 33 | #pragma warning disable INPC008 // Struct must not implement INotifyPropertyChanged 34 | Code violating the rule here 35 | #pragma warning restore INPC008 // Struct must not implement INotifyPropertyChanged 36 | ``` 37 | 38 | Or put this at the top of the file to disable all instances. 39 | ```C# 40 | #pragma warning disable INPC008 // Struct must not implement INotifyPropertyChanged 41 | ``` 42 | 43 | ### Via attribute `[SuppressMessage]`. 44 | 45 | ```C# 46 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 47 | "INPC008:Struct must not implement INotifyPropertyChanged", 48 | Justification = "Reason...")] 49 | ``` 50 | -------------------------------------------------------------------------------- /documentation/INPC009.md: -------------------------------------------------------------------------------- 1 | # INPC009 2 | ## Don't raise PropertyChanged for missing property 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC009 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [ArgumentAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/ArgumentAnalyzer.cs) 11 | | | [InvocationAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/InvocationAnalyzer.cs) 12 | 13 | 14 | ## Description 15 | 16 | Don't raise PropertyChanged for missing property. 17 | 18 | ## Motivation 19 | 20 | ADD MOTIVATION HERE 21 | 22 | ## How to fix violations 23 | 24 | ADD HOW TO FIX VIOLATIONS HERE 25 | 26 | 27 | ## Configure severity 28 | 29 | ### Via ruleset file. 30 | 31 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 32 | 33 | ### Via #pragma directive. 34 | ```C# 35 | #pragma warning disable INPC009 // Don't raise PropertyChanged for missing property 36 | Code violating the rule here 37 | #pragma warning restore INPC009 // Don't raise PropertyChanged for missing property 38 | ``` 39 | 40 | Or put this at the top of the file to disable all instances. 41 | ```C# 42 | #pragma warning disable INPC009 // Don't raise PropertyChanged for missing property 43 | ``` 44 | 45 | ### Via attribute `[SuppressMessage]`. 46 | 47 | ```C# 48 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 49 | "INPC009:Don't raise PropertyChanged for missing property", 50 | Justification = "Reason...")] 51 | ``` 52 | -------------------------------------------------------------------------------- /documentation/INPC010.md: -------------------------------------------------------------------------------- 1 | # INPC010 2 | ## The property gets and sets a different backing member 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC010 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [SetAccessorAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/SetAccessorAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | The property gets and sets a different backing member. 15 | 16 | ## Motivation 17 | 18 | ADD MOTIVATION HERE 19 | 20 | ## How to fix violations 21 | 22 | ADD HOW TO FIX VIOLATIONS HERE 23 | 24 | 25 | ## Configure severity 26 | 27 | ### Via ruleset file. 28 | 29 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 30 | 31 | ### Via #pragma directive. 32 | ```C# 33 | #pragma warning disable INPC010 // The property gets and sets a different backing member 34 | Code violating the rule here 35 | #pragma warning restore INPC010 // The property gets and sets a different backing member 36 | ``` 37 | 38 | Or put this at the top of the file to disable all instances. 39 | ```C# 40 | #pragma warning disable INPC010 // The property gets and sets a different backing member 41 | ``` 42 | 43 | ### Via attribute `[SuppressMessage]`. 44 | 45 | ```C# 46 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 47 | "INPC010:The property gets and sets a different backing member", 48 | Justification = "Reason...")] 49 | ``` 50 | -------------------------------------------------------------------------------- /documentation/INPC011.md: -------------------------------------------------------------------------------- 1 | # INPC011 2 | ## Don't shadow PropertyChanged event 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC011 7 | | Severity | Error 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [EventAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/EventAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | Don't shadow PropertyChanged event. 15 | 16 | ## Motivation 17 | 18 | ADD MOTIVATION HERE 19 | 20 | ## How to fix violations 21 | 22 | ADD HOW TO FIX VIOLATIONS HERE 23 | 24 | 25 | ## Configure severity 26 | 27 | ### Via ruleset file. 28 | 29 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 30 | 31 | ### Via #pragma directive. 32 | ```C# 33 | #pragma warning disable INPC011 // Don't shadow PropertyChanged event 34 | Code violating the rule here 35 | #pragma warning restore INPC011 // Don't shadow PropertyChanged event 36 | ``` 37 | 38 | Or put this at the top of the file to disable all instances. 39 | ```C# 40 | #pragma warning disable INPC011 // Don't shadow PropertyChanged event 41 | ``` 42 | 43 | ### Via attribute `[SuppressMessage]`. 44 | 45 | ```C# 46 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 47 | "INPC011:Don't shadow PropertyChanged event", 48 | Justification = "Reason...")] 49 | ``` 50 | -------------------------------------------------------------------------------- /documentation/INPC012.md: -------------------------------------------------------------------------------- 1 | # INPC012 2 | ## Don't use expression for raising PropertyChanged 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC012 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [ArgumentAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/ArgumentAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | Don't use expression for raising PropertyChanged. 15 | 16 | ## Motivation 17 | 18 | Wasteful for no reason given `nameof` and `[CallerMemberName]` were added to the language. 19 | 20 | ## How to fix violations 21 | 22 | Use `nameof` or `[CallerMemberName]` to extract the name to notify for. 23 | 24 | 25 | ## Configure severity 26 | 27 | ### Via ruleset file. 28 | 29 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 30 | 31 | ### Via #pragma directive. 32 | ```C# 33 | #pragma warning disable INPC012 // Don't use expression for raising PropertyChanged 34 | Code violating the rule here 35 | #pragma warning restore INPC012 // Don't use expression for raising PropertyChanged 36 | ``` 37 | 38 | Or put this at the top of the file to disable all instances. 39 | ```C# 40 | #pragma warning disable INPC012 // Don't use expression for raising PropertyChanged 41 | ``` 42 | 43 | ### Via attribute `[SuppressMessage]`. 44 | 45 | ```C# 46 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 47 | "INPC012:Don't use expression for raising PropertyChanged", 48 | Justification = "Reason...")] 49 | ``` 50 | -------------------------------------------------------------------------------- /documentation/INPC013.md: -------------------------------------------------------------------------------- 1 | # INPC013 2 | ## Use nameof 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC013 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [ArgumentAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/ArgumentAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | Use nameof. 15 | 16 | ## Motivation 17 | 18 | ADD MOTIVATION HERE 19 | 20 | ## How to fix violations 21 | 22 | ADD HOW TO FIX VIOLATIONS HERE 23 | 24 | 25 | ## Configure severity 26 | 27 | ### Via ruleset file. 28 | 29 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 30 | 31 | ### Via #pragma directive. 32 | ```C# 33 | #pragma warning disable INPC013 // Use nameof 34 | Code violating the rule here 35 | #pragma warning restore INPC013 // Use nameof 36 | ``` 37 | 38 | Or put this at the top of the file to disable all instances. 39 | ```C# 40 | #pragma warning disable INPC013 // Use nameof 41 | ``` 42 | 43 | ### Via attribute `[SuppressMessage]`. 44 | 45 | ```C# 46 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 47 | "INPC013:Use nameof", 48 | Justification = "Reason...")] 49 | ``` 50 | -------------------------------------------------------------------------------- /documentation/INPC014.md: -------------------------------------------------------------------------------- 1 | # INPC014 2 | ## Prefer setting backing field in constructor 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC014 7 | | Severity | Info 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [AssignmentAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/AssignmentAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | Prefer setting backing field in constructor. 15 | 16 | ## Motivation 17 | 18 | Setting the property often means a virtual `OnPropertyChangedCall` that can trigger warnings in other analyzers. 19 | Setting the field also has slightly better performance. 20 | 21 | Remarks: 22 | There may be desired side effects in the setter, this analyzer does not check for that so it is up to you to decide if setting the backing field is right. 23 | 24 | ## How to fix violations 25 | 26 | Use the code fix or change manually. 27 | 28 | 29 | ## Configure severity 30 | 31 | ### Via ruleset file. 32 | 33 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 34 | 35 | ### Via #pragma directive. 36 | ```C# 37 | #pragma warning disable INPC014 // Prefer setting backing field in constructor 38 | Code violating the rule here 39 | #pragma warning restore INPC014 // Prefer setting backing field in constructor 40 | ``` 41 | 42 | Or put this at the top of the file to disable all instances. 43 | ```C# 44 | #pragma warning disable INPC014 // Prefer setting backing field in constructor 45 | ``` 46 | 47 | ### Via attribute `[SuppressMessage]`. 48 | 49 | ```C# 50 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 51 | "INPC014:Prefer setting backing field in constructor", 52 | Justification = "Reason...")] 53 | ``` 54 | -------------------------------------------------------------------------------- /documentation/INPC015.md: -------------------------------------------------------------------------------- 1 | # INPC015 2 | ## Property is recursive 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC015 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [PropertyDeclarationAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/PropertyDeclarationAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | Property is recursive. 15 | 16 | ## Motivation 17 | 18 | Detects silly mistakes like: 19 | 20 | ```cs 21 | public class Foo 22 | { 23 | public int Bar => this.Bar; 24 | } 25 | ``` 26 | 27 | 28 | ## Configure severity 29 | 30 | ### Via ruleset file. 31 | 32 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 33 | 34 | ### Via #pragma directive. 35 | ```C# 36 | #pragma warning disable INPC015 // Property is recursive 37 | Code violating the rule here 38 | #pragma warning restore INPC015 // Property is recursive 39 | ``` 40 | 41 | Or put this at the top of the file to disable all instances. 42 | ```C# 43 | #pragma warning disable INPC015 // Property is recursive 44 | ``` 45 | 46 | ### Via attribute `[SuppressMessage]`. 47 | 48 | ```C# 49 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 50 | "INPC015:Property is recursive", 51 | Justification = "Reason...")] 52 | ``` 53 | 54 | -------------------------------------------------------------------------------- /documentation/INPC016.md: -------------------------------------------------------------------------------- 1 | # INPC016 2 | ## Notify after update 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC016 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [SetAccessorAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/SetAccessorAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | Notify after updating the backing field. 15 | 16 | ## Motivation 17 | 18 | ADD MOTIVATION HERE 19 | 20 | ## How to fix violations 21 | 22 | ADD HOW TO FIX VIOLATIONS HERE 23 | 24 | 25 | ## Configure severity 26 | 27 | ### Via ruleset file. 28 | 29 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 30 | 31 | ### Via #pragma directive. 32 | ```C# 33 | #pragma warning disable INPC016 // Notify after update 34 | Code violating the rule here 35 | #pragma warning restore INPC016 // Notify after update 36 | ``` 37 | 38 | Or put this at the top of the file to disable all instances. 39 | ```C# 40 | #pragma warning disable INPC016 // Notify after update 41 | ``` 42 | 43 | ### Via attribute `[SuppressMessage]`. 44 | 45 | ```C# 46 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 47 | "INPC016:Notify after update", 48 | Justification = "Reason...")] 49 | ``` 50 | -------------------------------------------------------------------------------- /documentation/INPC017.md: -------------------------------------------------------------------------------- 1 | # INPC017 2 | ## Backing field name must match 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC017 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [PropertyDeclarationAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/PropertyDeclarationAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | Backing field name must match. 15 | 16 | ## Motivation 17 | 18 | ADD MOTIVATION HERE 19 | 20 | ## How to fix violations 21 | 22 | ADD HOW TO FIX VIOLATIONS HERE 23 | 24 | 25 | ## Configure severity 26 | 27 | ### Via ruleset file. 28 | 29 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 30 | 31 | ### Via #pragma directive. 32 | ```C# 33 | #pragma warning disable INPC017 // Backing field name must match 34 | Code violating the rule here 35 | #pragma warning restore INPC017 // Backing field name must match 36 | ``` 37 | 38 | Or put this at the top of the file to disable all instances. 39 | ```C# 40 | #pragma warning disable INPC017 // Backing field name must match 41 | ``` 42 | 43 | ### Via attribute `[SuppressMessage]`. 44 | 45 | ```C# 46 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 47 | "INPC017:Backing field name must match", 48 | Justification = "Reason...")] 49 | ``` 50 | -------------------------------------------------------------------------------- /documentation/INPC018.md: -------------------------------------------------------------------------------- 1 | # INPC018 2 | ## PropertyChanged invoker should be protected when the class is not sealed 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC018 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [MethodDeclarationAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/MethodDeclarationAnalyzer.cs) 11 | 12 | 13 | ## Description 14 | 15 | PropertyChanged invoker should be protected when the class is not sealed. 16 | 17 | ## Motivation 18 | 19 | ADD MOTIVATION HERE 20 | 21 | ## How to fix violations 22 | 23 | ADD HOW TO FIX VIOLATIONS HERE 24 | 25 | 26 | ## Configure severity 27 | 28 | ### Via ruleset file. 29 | 30 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 31 | 32 | ### Via #pragma directive. 33 | ```C# 34 | #pragma warning disable INPC018 // PropertyChanged invoker should be protected when the class is not sealed 35 | Code violating the rule here 36 | #pragma warning restore INPC018 // PropertyChanged invoker should be protected when the class is not sealed 37 | ``` 38 | 39 | Or put this at the top of the file to disable all instances. 40 | ```C# 41 | #pragma warning disable INPC018 // PropertyChanged invoker should be protected when the class is not sealed 42 | ``` 43 | 44 | ### Via attribute `[SuppressMessage]`. 45 | 46 | ```C# 47 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 48 | "INPC018:PropertyChanged invoker should be protected when the class is not sealed", 49 | Justification = "Reason...")] 50 | ``` 51 | -------------------------------------------------------------------------------- /documentation/INPC019.md: -------------------------------------------------------------------------------- 1 | # INPC019 2 | ## Getter should return backing field 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC019 7 | | Severity | Info 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [PropertyDeclarationAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/PropertyDeclarationAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | Getter should return backing field. 15 | 16 | ## Motivation 17 | 18 | ADD MOTIVATION HERE 19 | 20 | ## How to fix violations 21 | 22 | ADD HOW TO FIX VIOLATIONS HERE 23 | 24 | 25 | ## Configure severity 26 | 27 | ### Via ruleset file. 28 | 29 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 30 | 31 | ### Via #pragma directive. 32 | ```C# 33 | #pragma warning disable INPC019 // Getter should return backing field 34 | Code violating the rule here 35 | #pragma warning restore INPC019 // Getter should return backing field 36 | ``` 37 | 38 | Or put this at the top of the file to disable all instances. 39 | ```C# 40 | #pragma warning disable INPC019 // Getter should return backing field 41 | ``` 42 | 43 | ### Via attribute `[SuppressMessage]`. 44 | 45 | ```C# 46 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 47 | "INPC019:Getter should return backing field", 48 | Justification = "Reason...")] 49 | ``` 50 | -------------------------------------------------------------------------------- /documentation/INPC020.md: -------------------------------------------------------------------------------- 1 | # INPC020 2 | ## Prefer expression body accessor 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC020 7 | | Severity | Info 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [PropertyDeclarationAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/PropertyDeclarationAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | Prefer expression body accessor. 15 | 16 | ## Motivation 17 | 18 | ADD MOTIVATION HERE 19 | 20 | ## How to fix violations 21 | 22 | ADD HOW TO FIX VIOLATIONS HERE 23 | 24 | 25 | ## Configure severity 26 | 27 | ### Via ruleset file. 28 | 29 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 30 | 31 | ### Via #pragma directive. 32 | ```C# 33 | #pragma warning disable INPC020 // Prefer expression body accessor 34 | Code violating the rule here 35 | #pragma warning restore INPC020 // Prefer expression body accessor 36 | ``` 37 | 38 | Or put this at the top of the file to disable all instances. 39 | ```C# 40 | #pragma warning disable INPC020 // Prefer expression body accessor 41 | ``` 42 | 43 | ### Via attribute `[SuppressMessage]`. 44 | 45 | ```C# 46 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 47 | "INPC020:Prefer expression body accessor", 48 | Justification = "Reason...")] 49 | ``` 50 | -------------------------------------------------------------------------------- /documentation/INPC021.md: -------------------------------------------------------------------------------- 1 | # INPC021 2 | ## Setter should set backing field 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC021 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [SetAccessorAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/SetAccessorAnalyzer.cs) 11 | 12 | ## Description 13 | 14 | Setter should set backing field. 15 | 16 | ## Motivation 17 | 18 | In the sample below not assigning the backing field is likely a bug. 19 | 20 | ```cs 21 | public int P 22 | { 23 | get => this.p; 24 | set 25 | { 26 | if (value == this.p) 27 | { 28 | return; 29 | } 30 | 31 | // this.p = value; 32 | this.OnPropertyChanged(); 33 | } 34 | } 35 | ``` 36 | 37 | ## How to fix violations 38 | 39 | Assign the backing member. 40 | 41 | 42 | ## Configure severity 43 | 44 | ### Via ruleset file. 45 | 46 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 47 | 48 | ### Via #pragma directive. 49 | ```C# 50 | #pragma warning disable INPC021 // Setter should set backing field 51 | Code violating the rule here 52 | #pragma warning restore INPC021 // Setter should set backing field 53 | ``` 54 | 55 | Or put this at the top of the file to disable all instances. 56 | ```C# 57 | #pragma warning disable INPC021 // Setter should set backing field 58 | ``` 59 | 60 | ### Via attribute `[SuppressMessage]`. 61 | 62 | ```C# 63 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 64 | "INPC021:Setter should set backing field", 65 | Justification = "Reason...")] 66 | ``` 67 | -------------------------------------------------------------------------------- /documentation/INPC022.md: -------------------------------------------------------------------------------- 1 | # INPC022 2 | ## Comparison should be with backing field 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC022 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [SetAccessorAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/SetAccessorAnalyzer.cs) 11 | 12 | 13 | ## Description 14 | 15 | Comparison should be with backing field. 16 | 17 | ## Motivation 18 | 19 | ```cs 20 | public int P 21 | { 22 | get => this.p; 23 | set 24 | { 25 | if (value == ↓this.f) 26 | { 27 | return; 28 | } 29 | 30 | this.p = value; 31 | this.OnPropertyChanged(); 32 | } 33 | } 34 | ``` 35 | 36 | In the above example equality is checked vs field `f` when field `p` is later assigned. 37 | 38 | ## How to fix violations 39 | 40 | The code fix offers 41 | 42 | 43 | ## Configure severity 44 | 45 | ### Via ruleset file. 46 | 47 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 48 | 49 | ### Via #pragma directive. 50 | ```C# 51 | #pragma warning disable INPC022 // Comparison should be with backing field 52 | Code violating the rule here 53 | #pragma warning restore INPC022 // Comparison should be with backing field 54 | ``` 55 | 56 | Or put this at the top of the file to disable all instances. 57 | ```C# 58 | #pragma warning disable INPC022 // Comparison should be with backing field 59 | ``` 60 | 61 | ### Via attribute `[SuppressMessage]`. 62 | 63 | ```C# 64 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 65 | "INPC022:Comparison should be with backing field", 66 | Justification = "Reason...")] 67 | ``` 68 | -------------------------------------------------------------------------------- /documentation/INPC023.md: -------------------------------------------------------------------------------- 1 | # INPC023 2 | ## Don't use instance equals in setter 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC023 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [SetAccessorAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/SetAccessorAnalyzer.cs) 11 | 12 | 13 | ## Description 14 | 15 | Instance equals could throw NullReferenceException. 16 | 17 | ## Motivation 18 | 19 | Using instance equality in the sample below throws `NullReferenceException` if property is assigned with null. 20 | 21 | ```cs 22 | public int? P 23 | { 24 | get => this.p; 25 | set 26 | { 27 | if (↓value.Equals(this.p)) 28 | { 29 | return; 30 | } 31 | 32 | this.p = value; 33 | this.OnPropertyChanged(); 34 | } 35 | } 36 | ``` 37 | 38 | ## How to fix violations 39 | 40 | Use the code fix to change it to 41 | 42 | ```cs 43 | public int? P 44 | { 45 | get => this.p; 46 | set 47 | { 48 | if (value == this.p) 49 | { 50 | return; 51 | } 52 | 53 | this.p = value; 54 | this.OnPropertyChanged(); 55 | } 56 | } 57 | ``` 58 | 59 | 60 | ## Configure severity 61 | 62 | ### Via ruleset file. 63 | 64 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 65 | 66 | ### Via #pragma directive. 67 | ```C# 68 | #pragma warning disable INPC023 // Don't use instance equals in setter 69 | Code violating the rule here 70 | #pragma warning restore INPC023 // Don't use instance equals in setter 71 | ``` 72 | 73 | Or put this at the top of the file to disable all instances. 74 | ```C# 75 | #pragma warning disable INPC023 // Don't use instance equals in setter 76 | ``` 77 | 78 | ### Via attribute `[SuppressMessage]`. 79 | 80 | ```C# 81 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 82 | "INPC023:Don't use instance equals in setter", 83 | Justification = "Reason...")] 84 | ``` 85 | -------------------------------------------------------------------------------- /documentation/INPC024.md: -------------------------------------------------------------------------------- 1 | # INPC024 2 | ## ReferenceEquals is always false for value types 3 | 4 | | Topic | Value 5 | | :-- | :-- 6 | | Id | INPC024 7 | | Severity | Warning 8 | | Enabled | True 9 | | Category | PropertyChangedAnalyzers.PropertyChanged 10 | | Code | [SetAccessorAnalyzer](https://github.com/DotNetAnalyzers/PropertyChangedAnalyzers/blob/master/PropertyChangedAnalyzers/Analyzers/SetAccessorAnalyzer.cs) 11 | 12 | 13 | ## Description 14 | 15 | ReferenceEquals is always false for value types. 16 | 17 | ## Motivation 18 | 19 | Using `ReferenceEquals` in the sample below is a bug as it is always false. 20 | 21 | ```cs 22 | public int P 23 | { 24 | get => this.p; 25 | set 26 | { 27 | if (ReferenceEquals(value, this.p)) 28 | { 29 | return; 30 | } 31 | 32 | this.p = value; 33 | this.OnPropertyChanged(); 34 | } 35 | } 36 | ``` 37 | 38 | ## How to fix violations 39 | 40 | Use the code fix to change it to: 41 | 42 | ```cs 43 | public int P 44 | { 45 | get => this.p; 46 | set 47 | { 48 | if (value == this.p) 49 | { 50 | return; 51 | } 52 | 53 | this.p = value; 54 | this.OnPropertyChanged(); 55 | } 56 | } 57 | ``` 58 | 59 | 60 | ## Configure severity 61 | 62 | ### Via ruleset file. 63 | 64 | Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). 65 | 66 | ### Via #pragma directive. 67 | ```C# 68 | #pragma warning disable INPC024 // ReferenceEquals is always false for value types 69 | Code violating the rule here 70 | #pragma warning restore INPC024 // ReferenceEquals is always false for value types 71 | ``` 72 | 73 | Or put this at the top of the file to disable all instances. 74 | ```C# 75 | #pragma warning disable INPC024 // ReferenceEquals is always false for value types 76 | ``` 77 | 78 | ### Via attribute `[SuppressMessage]`. 79 | 80 | ```C# 81 | [System.Diagnostics.CodeAnalysis.SuppressMessage("PropertyChangedAnalyzers.PropertyChanged", 82 | "INPC024:ReferenceEquals is always false for value types", 83 | Justification = "Reason...")] 84 | ``` 85 | --------------------------------------------------------------------------------