├── .config └── dotnet-tools.json ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug-template.md │ └── config.yml └── pull-request-template.md ├── .gitignore ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── appveyor.yml ├── build.csx ├── icon-small.png ├── icon.png ├── icon.svg └── src ├── .editorconfig ├── PropertyChanged.SourceGenerator.Benchmarks ├── Program.cs └── PropertyChanged.SourceGenerator.Benchmarks.csproj ├── PropertyChanged.SourceGenerator.UnitTests ├── AccessibilityTests.HandlesGetterAndSetterEquallyAccessible_SomeViewModel.verified.cs ├── AccessibilityTests.HandlesGetterLessAccessibleThanSetter_SomeViewModel.verified.cs ├── AccessibilityTests.HandlesGetterMoreAccessibleThanSetter_SomeViewModel.verified.cs ├── AccessibilityTests.HandlesInternalGetterProtectedSetter_SomeViewModel.verified.cs ├── AccessibilityTests.HandlesProtectedGetterInternalSetter_SomeViewModel.verified.cs ├── AccessibilityTests.RaisesIfBackingFieldIsReadOnly_SomeViewModel.verified.cs ├── AccessibilityTests.RaisesIfBackingPropertyIsGetterOnly_SomeViewModel.verified.cs ├── AccessibilityTests.RaisesIfBackingPropertyIsSetterOnly_SomeViewModel.verified.cs ├── AccessibilityTests.cs ├── AdditionalPropertyGenerationTests.AddsVirtual_SomeViewModel.verified.cs ├── AdditionalPropertyGenerationTests.cs ├── AlsoNotifyTests.HandlesPathologicalAttributeCases_SomeViewModel.verified.cs ├── AlsoNotifyTests.HandlesStandardNonPropertyNames_PropertyChanged.SourceGenerator.Internal.EventArgsCache.verified.cs ├── AlsoNotifyTests.HandlesStandardNonPropertyNames_SomeViewModel.verified.cs ├── AlsoNotifyTests.NotifiesGeneratedPropertyOnBaseClass_Derived.verified.cs ├── AlsoNotifyTests.NotifiesGeneratedProperty_SomeViewModel.verified.cs ├── AlsoNotifyTests.NotifiesPreExistingPropertyOnBaseClass_Derived.verified.cs ├── AlsoNotifyTests.NotifiesPreExistingProperty_SomeViewModel.verified.cs ├── AlsoNotifyTests.PassesNullForOldAndNewValueIfPropertyDoesNotExist_SomeViewModel.verified.cs ├── AlsoNotifyTests.PassesOldAndNewValue_SomeViewModel.verified.cs ├── AlsoNotifyTests.RaisesIfAutoNotifyingSelf_SomeViewModel.verified.cs ├── AlsoNotifyTests.RaisesIfPropertyDefinedOnChildClass_Base.verified.cs ├── AlsoNotifyTests.cs ├── AutoDependsOnTests.cs ├── DependsOnTests.HandlesDerivedTypeAutoDependsOn_Derived.verified.cs ├── DependsOnTests.HandlesDerivedTypeManualDependsOn_Derived.verified.cs ├── DependsOnTests.cs ├── DocTests.CopiesDocs_SomeViewModel.verified.cs ├── DocTests.GeneratesOnPropertyChangedOrChangingDocNoOldAndNew_SomeViewModel.verified.cs ├── DocTests.GeneratesOnPropertyChangedOrChangingDocOldAndNew_SomeViewModel.verified.cs ├── DocTests.HandlesMalformedXml_SomeViewModel.verified.cs ├── DocTests.cs ├── Framework │ ├── DiagnosticResult.cs │ ├── DiagnosticVerifier.cs │ ├── RemoveDocumentationRewriter.cs │ ├── RemoveInpcMembersRewriter.cs │ ├── RemovePropertiesRewriter.cs │ └── TestsBase.cs ├── IsChangedTests.GeneratesIsChangedOnBaseClass_Derived.verified.cs ├── IsChangedTests.GeneratesIsChangedToGeneratedProperty_SomeViewModel.verified.cs ├── IsChangedTests.GeneratesIsChangedToProperty_SomeViewModel.verified.cs ├── IsChangedTests.IgnoresIfGeneratedIsChangedPropertyOnBaseClassHasPrivateSetter_Derived.verified.cs ├── IsChangedTests.IgnoresIfIsChangedPropertyOnBaseClassHasPrivateSetter_Derived.verified.cs ├── IsChangedTests.cs ├── NullableTypeTests.DoesNotGenerateNullableEventIfInFileNullableContext_SomeViewModel.verified.cs ├── NullableTypeTests.GeneratesNullableEventIfInCompilationNullableContext_SomeViewModel.verified.cs ├── NullableTypeTests.GeneratesNullablePropertiesIfInCompilationNullableContext_SomeViewModel.verified.cs ├── NullableTypeTests.GeneratesNullablePropertiesIfInFilenNullableContext_SomeViewModel.verified.cs ├── NullableTypeTests.cs ├── OnAnyPropertyChangedTests.DoesNotGenerateForOverriddenMethod_Base.verified.cs ├── OnAnyPropertyChangedTests.DoesNotGenerateForOverriddenMethod_Derived.verified.cs ├── OnAnyPropertyChangedTests.GeneratesForShadowedMethod_Derived.verified.cs ├── OnAnyPropertyChangedTests.GeneratesOldAndNewWhenAlsoGeneratingRaisePropertyChanged_SomeViewModel.verified.cs ├── OnAnyPropertyChangedTests.GeneratesOldAndNewWhenOverridingOldAndNew_Derived.verified.cs ├── OnAnyPropertyChangedTests.GeneratesOldAndNewWhenOverridingParameterless_Derived.verified.cs ├── OnAnyPropertyChangedTests.GeneratesParameterlessWhenAlsoGeneratingRaisePropertyChanged_SomeViewModel.verified.cs ├── OnAnyPropertyChangedTests.GeneratesParameterlessWhenOverridingOldAndNew_Derived.verified.cs ├── OnAnyPropertyChangedTests.GeneratesParameterlessWhenOverridingParameterless_Derived.verified.cs ├── OnAnyPropertyChangedTests.cs ├── OnAnyPropertyChangingTests.DoesNotGenerateForOverriddenMethod_Base.verified.cs ├── OnAnyPropertyChangingTests.DoesNotGenerateForOverriddenMethod_Derived.verified.cs ├── OnAnyPropertyChangingTests.GeneratesForShadowedMethod_Derived.verified.cs ├── OnAnyPropertyChangingTests.GeneratesOldWhenAlsoGeneratingRaisePropertyChanging_SomeViewModel.verified.cs ├── OnAnyPropertyChangingTests.GeneratesOldWhenOverridingOld_Derived.verified.cs ├── OnAnyPropertyChangingTests.GeneratesOldWhenOverridingParameterless_Derived.verified.cs ├── OnAnyPropertyChangingTests.GeneratesParameterlessWhenAlsoGeneratingRaisePropertyChanging_SomeViewModel.verified.cs ├── OnAnyPropertyChangingTests.GeneratesParameterlessWhenOverridingOld_Derived.verified.cs ├── OnAnyPropertyChangingTests.GeneratesParameterlessWhenOverridingParameterless_Derived.verified.cs ├── OnAnyPropertyChangingTests.cs ├── OnPropertyNameChangedTests.DoesNotCallInaccessibleAlsoNotifyOnBaseClass_Derived.verified.cs ├── OnPropertyNameChangedTests.DoesNotGenerateAlsoNotifyNonCallable_SomeViewModel.verified.cs ├── OnPropertyNameChangedTests.DoesNotGenerateAlsoNotifyWithPropertyOnBaseClassAndMethodOnDerived_Derived.verified.cs ├── OnPropertyNameChangedTests.DoesNotMatchMethodWithDifferingParameterTypes_SomeViewModel.verified.cs ├── OnPropertyNameChangedTests.GenerateParameterlessRaise_SomeViewModel.verified.cs ├── OnPropertyNameChangedTests.GeneratesAlsoNotifyCallableParameterless_SomeViewModel.verified.cs ├── OnPropertyNameChangedTests.GeneratesAlsoNotifyCallableParameters_SomeViewModel.verified.cs ├── OnPropertyNameChangedTests.GeneratesAlsoNotifyOnBaseClass_Derived.verified.cs ├── OnPropertyNameChangedTests.GeneratesAutoDependsOn_SomeViewModel.verified.cs ├── OnPropertyNameChangedTests.GeneratesDependsOn_SomeViewModel.verified.cs ├── OnPropertyNameChangedTests.GeneratesOldAndNewWithMatchingDataType_SomeViewModel.verified.cs ├── OnPropertyNameChangedTests.GeneratesOldAndNewWithParentDataType_SomeViewModel.verified.cs ├── OnPropertyNameChangedTests.cs ├── OnPropertyNameChangingTests.DoesNotCallInaccessibleAlsoNotifyOnBaseClass_Derived.verified.cs ├── OnPropertyNameChangingTests.DoesNotGenerateAlsoNotifyWithPropertyOnBaseClassAndMethodOnDerived_Derived.verified.cs ├── OnPropertyNameChangingTests.GenerateParameterlessRaise_SomeViewModel.verified.cs ├── OnPropertyNameChangingTests.GeneratesAlsoNotifyCallableParameterless_SomeViewModel.verified.cs ├── OnPropertyNameChangingTests.GeneratesAlsoNotifyCallableParameters_SomeViewModel.verified.cs ├── OnPropertyNameChangingTests.GeneratesAlsoNotifyOnBaseClass_Derived.verified.cs ├── OnPropertyNameChangingTests.GeneratesAutoDependsOn_SomeViewModel.verified.cs ├── OnPropertyNameChangingTests.GeneratesDependsOn_SomeViewModel.verified.cs ├── OnPropertyNameChangingTests.GeneratesOldAndNewWithParentDataType_SomeViewModel.verified.cs ├── OnPropertyNameChangingTests.GeneratesOldWithMatchingDataType_SomeViewModel.verified.cs ├── OnPropertyNameChangingTests.cs ├── PropertyAttributeTests.AddsLeadingAndTrailingBracketsIfOmitted_SomeViewModel.verified.cs ├── PropertyAttributeTests.CopiesAttributeOntoGeneratedProperty_SomeViewModel.verified.cs ├── PropertyAttributeTests.cs ├── PropertyChanged.SourceGenerator.UnitTests.csproj ├── PropertyNameTests.ReportsCollisionWithExplicitNamesInBaseClass_Base.verified.cs ├── PropertyNameTests.ReportsCollisionWithExplicitNamesInBaseClass_Derived.verified.cs ├── PropertyNameTests.ReportsCollisionWithTwoExplicitNames_SomeViewModel.verified.cs ├── PropertyNameTests.ReportsNameCollisionExplicit_SomeViewModel.verified.cs ├── PropertyNameTests.ReportsNameCollisionInBaseClass_SomeViewModel.verified.cs ├── PropertyNameTests.ReportsNameCollisionInferred_SomeViewModel.verified.cs ├── PropertyNameTests.UsesExplicitNameIfGiven_SomeViewModel.verified.cs ├── PropertyNameTests.cs ├── RaisePropertyChangedCallTests.DoesNotGenerateInpcInterfaceIfAlreadySpecified_SomeViewModel.verified.cs ├── RaisePropertyChangedCallTests.FindsAndCallsMethodWithEventArgsAndOldAndNewValues_SomeViewModel.verified.cs ├── RaisePropertyChangedCallTests.FindsAndCallsMethodWithEventArgs_Derived.verified.cs ├── RaisePropertyChangedCallTests.FindsAndCallsMethodWithStringNameAndOldAndNewValues_SomeViewModel.verified.cs ├── RaisePropertyChangedCallTests.FindsAndCallsMethodWithStringName_SomeViewModel.verified.cs ├── RaisePropertyChangedCallTests.FindsExplicitlyImplementedEvent_SomeViewModel.verified.cs ├── RaisePropertyChangedCallTests.FindsGenericBaseClasses_B.verified.cs ├── RaisePropertyChangedCallTests.GeneratesInpcInterfaceIfNotSpecified_SomeViewModel.verified.cs ├── RaisePropertyChangedCallTests.SearchesForMethodFromTopOfTypeHierarchy_B.verified.cs ├── RaisePropertyChangedCallTests.SearchesForMethodFromTopOfTypeHierarchy_C.verified.cs ├── RaisePropertyChangedCallTests.TakesNameFromPropertyChangingIfPossible_SomeViewModel.verified.cs ├── RaisePropertyChangedCallTests.cs ├── RaisePropertyChangedDefinitionTests.CallsOnPropertyNameChangedNoOldAndNew_Derived.verified.cs ├── RaisePropertyChangedDefinitionTests.CallsOnPropertyNameChangedOldAndNew_Derived.verified.cs ├── RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=internal.verified.cs ├── RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=private protected.verified.cs ├── RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected internal.verified.cs ├── RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected.verified.cs ├── RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=public.verified.cs ├── RaisePropertyChangedDefinitionTests.DefinesOverrideEventArgsNoOldAndNew_Derived.verified.cs ├── RaisePropertyChangedDefinitionTests.DefinesOverrideEventArgsOldAndNew_Derived.verified.cs ├── RaisePropertyChangedDefinitionTests.DefinesOverrideStringNoOldAndNew_Derived.verified.cs ├── RaisePropertyChangedDefinitionTests.DefinesOverrideStringOldAndNew_Derived.verified.cs ├── RaisePropertyChangedDefinitionTests.DefinesVirtual_Derived.verified.cs ├── RaisePropertyChangedDefinitionTests.GeneratesEventAndRaisePropertyChangedIfNotDefined_SomeViewModel.verified.cs ├── RaisePropertyChangedDefinitionTests.GeneratesRaisePropertyChangedIfNotDefined_SomeViewModel.verified.cs ├── RaisePropertyChangedDefinitionTests.HandlesSealedClass_SomeViewModel.verified.cs ├── RaisePropertyChangedDefinitionTests.RaisesIfNonVirtualBaseMethodAndOverrideRequired_Derived.verified.cs ├── RaisePropertyChangedDefinitionTests.cs ├── RaisePropertyChangingCallTests.DoesNotGenerateInpcInterfaceIfAlreadySpecified_SomeViewModel.verified.cs ├── RaisePropertyChangingCallTests.DoesNotGenerateInpcInterfaceIfNotSpecified_SomeViewModel.verified.cs ├── RaisePropertyChangingCallTests.FindsAndCallsMethodWithEventArgsAndOldValue_SomeViewModel.verified.cs ├── RaisePropertyChangingCallTests.FindsAndCallsMethodWithEventArgs_Derived.verified.cs ├── RaisePropertyChangingCallTests.FindsAndCallsMethodWithStringNameAndOldValue_SomeViewModel.verified.cs ├── RaisePropertyChangingCallTests.FindsAndCallsMethodWithStringName_SomeViewModel.verified.cs ├── RaisePropertyChangingCallTests.FindsExplicitlyImplementedEvent_SomeViewModel.verified.cs ├── RaisePropertyChangingCallTests.FindsGenericBaseClasses_B.verified.cs ├── RaisePropertyChangingCallTests.SearchesForMethodFromTopOfTypeHierarchy_B.verified.cs ├── RaisePropertyChangingCallTests.SearchesForMethodFromTopOfTypeHierarchy_C.verified.cs ├── RaisePropertyChangingCallTests.TakesNameFromPropertyChangedIfPossible_SomeViewModel.verified.cs ├── RaisePropertyChangingCallTests.cs ├── RaisePropertyChangingDefinitionTests.CallsOnPropertyNameChangingNoOld_Derived.verified.cs ├── RaisePropertyChangingDefinitionTests.CallsOnPropertyNameChangingOld_Derived.verified.cs ├── RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=internal.verified.cs ├── RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=private protected.verified.cs ├── RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected internal.verified.cs ├── RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected.verified.cs ├── RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=public.verified.cs ├── RaisePropertyChangingDefinitionTests.DefinesOverrideEventArgsNoOld_Derived.verified.cs ├── RaisePropertyChangingDefinitionTests.DefinesOverrideEventArgsOld_Derived.verified.cs ├── RaisePropertyChangingDefinitionTests.DefinesOverrideStringNoOld_Derived.verified.cs ├── RaisePropertyChangingDefinitionTests.DefinesOverrideStringOld_Derived.verified.cs ├── RaisePropertyChangingDefinitionTests.DefinesVirtual_Derived.verified.cs ├── RaisePropertyChangingDefinitionTests.DoesNothingIfTypeDoesNotImplementInterface_SomeViewModel.verified.cs ├── RaisePropertyChangingDefinitionTests.GeneratesEventAndRaisePropertyChangingIfNotDefined_SomeViewModel.verified.cs ├── RaisePropertyChangingDefinitionTests.GeneratesRaisePropertyChangingIfNotDefined_SomeViewModel.verified.cs ├── RaisePropertyChangingDefinitionTests.RaisesIfNonVirtualBaseMethodAndOverrideRequired_Derived.verified.cs ├── RaisePropertyChangingDefinitionTests.cs ├── TypeGenerationTests.GeneratesNamespace_Test.Foo.SomeViewModel.verified.cs ├── TypeGenerationTests.HandlesBadlyNamedGenericTypes_SomeViewModel.verified.cs ├── TypeGenerationTests.HandlesGenericTypesWithConstraints_SomeViewModel.verified.cs ├── TypeGenerationTests.HandlesPartialNestedTypes_A.B.C.verified.cs ├── TypeGenerationTests.HandlesRecord_SomeViewModel.verified.cs ├── TypeGenerationTests.HandlesStruct_SomeViewModel.verified.cs ├── TypeGenerationTests.HandlesTwoClassesSameNameDifferentNamespaces_NS1.SomeViewModel.verified.cs ├── TypeGenerationTests.HandlesTwoClassesSameNameDifferentNamespaces_NS2.SomeViewModel.verified.cs └── TypeGenerationTests.cs ├── PropertyChanged.SourceGenerator.Vsix ├── PropertyChanged.SourceGenerator.Vsix.csproj └── source.extension.vsixmanifest ├── PropertyChanged.SourceGenerator.sln ├── PropertyChanged.SourceGenerator ├── AlwaysFalseEqualityComparer.cs ├── Analysis │ ├── AlsoNotifyMember.cs │ ├── Analyser.cs │ ├── AnalyserInput.cs │ ├── Analyser_AlsoNotify.cs │ ├── Analyser_DependsOn.cs │ ├── Analyser_IsChanged.cs │ ├── IMember.cs │ ├── InterfaceAnalyser.cs │ ├── InterfaceAnalysis.cs │ ├── MemberAnalysis.cs │ ├── OnPropertyNameChangedInfo.cs │ ├── PropertyChangedInterfaceAnalyser.cs │ ├── PropertyChangingInterfaceAnalyser.cs │ ├── RaisePropertyChangedMethodType.cs │ ├── RaisePropertyChangedOrChangingMethodSignature.cs │ ├── TypeAnalysis.cs │ ├── TypeAnalysisLookups.cs │ └── Utils.cs ├── BetterIndentedTextWriter.cs ├── Configuration.cs ├── ConfigurationParser.cs ├── DiagnosticReporter.cs ├── EquatableArray.cs ├── EventArgs │ ├── EventArgsCache.cs │ ├── EventArgsCacheBuilder.cs │ ├── EventArgsCacheKey.cs │ └── EventArgsCacheLookup.cs ├── EventArgsCacheGenerator.cs ├── Generator.cs ├── Polyfills │ ├── InterpolatedStringHandlerArgumentAttribute.cs │ ├── InterpolatedStringHandlerAttribute.cs │ ├── NullableAttributes.cs │ ├── RequiredMemberAttribute.cs │ └── SetsRequiredMembersAttribute.cs ├── PropertyChanged.SourceGenerator.csproj ├── PropertyChangedSourceGenerator.cs ├── ReadOnlyEquatableList.cs ├── StringConstants.cs └── SymbolDisplayFormats.cs └── Sandbox ├── .editorconfig ├── Program.cs └── Sandbox.csproj /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-reportgenerator-globaltool": { 6 | "version": "4.8.9", 7 | "commands": [ 8 | "reportgenerator" 9 | ] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Report a bug 3 | about: If you've definitely found something wrong, use this. Not sure? Open a discussion. 4 | --- 5 | 6 | **Description** 7 | A clear and concise description of what the bug is. Use screenshots as necessary. 8 | 9 | **To Reproduce** 10 | Code to reproduce the bug, which someone else can run. 11 | 12 | **Version Info** 13 | - PropertyChanged.SourceGenerator version: [e.g. 1.2.3] 14 | - Roslyn Version (add `#error version` to your code, and see what the error message is) 15 | 16 | **Additional Info** 17 | Add any other context about the problem here. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Open a Discussion 4 | url: https://github.com/canton7/PropertyChanged.SourceGenerator/discussions/new 5 | about: If you've got a question, suggestion, or something you're not sure about, please open a discussion. 6 | -------------------------------------------------------------------------------- /.github/pull-request-template.md: -------------------------------------------------------------------------------- 1 | **Checklist** 2 | 3 | Thanks for contributing! Before we start, there are a few things we need to check: 4 | 5 | 1. You've discussed your intention to work on this feature/bug fix. 6 | 2. This feature branch is based on develop (**not** master). The bar above should say "base: develop". 7 | 8 | Thanks! 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | coverage.xml 3 | .vs 4 | bin 5 | obj 6 | NuGet 7 | *.received.cs 8 | BenchmarkDotNet.Artifacts 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | v1.1.1 2 | ------ 3 | 4 | - Fix DependsOn not working when depending on a generated property from a base type (#47) 5 | 6 | 7 | v1.1.0 8 | ------ 9 | 10 | - Restructure as an incremental source generator 11 | 12 | 13 | v1.0.8 14 | ------ 15 | 16 | - Support the user explicitly implementing the `PropertyChanged`/`PropertyChanging` event (#38) 17 | 18 | v1.0.7 19 | ------ 20 | 21 | - Allow automatic property depenency resolution to be disabled project-wide (#36) 22 | - Tweak what errors are raised when a suitable method to raise the PropertyChanged event could not be found (#33) 23 | - Properly support explicitly-implemented properties (#35) 24 | - Support generating virtual properties (#27) 25 | - Add `` to generated events (#29) 26 | - Fix README documentation on remove_{prefixes/suffixes} (#25) 27 | 28 | v1.0.6 29 | ------ 30 | 31 | - Don't crash when analysing a property which refers to itself (#24) 32 | 33 | v1.0.5 34 | ------ 35 | 36 | - Support placing attributes on generated properties with `[PropertyAttribute]` 37 | 38 | v1.0.4 39 | ------ 40 | 41 | - Add support for `INotifyPropertyChanging` 42 | - Generate doc comments on `OnPropertyChanged`/`OnPropertyChanging` 43 | 44 | v1.0.3 45 | ------ 46 | 47 | - Handle malformed XML doc comments 48 | - Don't propagate crashes to Roslyn, as it gets stuck in the "Generator is not generating files" state 49 | - Add README to NuGet package 50 | 51 | v1.0.2 52 | ------ 53 | 54 | - Correctly handle sealed types (#11) 55 | - Copy XML documentation from the field to generated property 56 | - Support automatic/manual `DependsOn` properties which refer to other properties 57 | - Support automatic/manual `DependsOn` with properties defined in the base class 58 | - Introduce the `OnAnyPropertyChanged` hook: using this is now recommended above overriding `OnPropertyChanged` 59 | 60 | v1.0.1 61 | ------ 62 | 63 | - Correctly detect generic base types (#3) 64 | - Fix handling partial inner classes (#2) 65 | - Fix crash when two types with the same name exist in different namespaces (#4) 66 | - README fixes and improvements 67 | 68 | v1.0.0 69 | ------ 70 | 71 | - Initial release 72 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Antony Male 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 13 | all 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 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.0.0.{build} 2 | image: Visual Studio 2022 3 | install: 4 | - ps: | 5 | $tagVersion = $($(git describe $(git rev-list --tags --max-count=1 origin/master)).TrimStart("v")) 6 | if ($env:appveyor_repo_tag -eq "true") 7 | { 8 | echo "Build triggered by a tag" 9 | $version = $tagVersion 10 | } 11 | else 12 | { 13 | $version = "$tagVersion.$env:appveyor_build_number" 14 | } 15 | echo $version 16 | Update-AppveyorBuild -Version $version 17 | & dotnet tool install -g dotnet-script 18 | 19 | build_script: 20 | - cmd: dotnet script build.csx -- package --version=%APPVEYOR_BUILD_VERSION% 21 | test_script: 22 | - cmd: dotnet script build.csx -- test 23 | artifacts: 24 | - path: '**/*.nupkg' 25 | -------------------------------------------------------------------------------- /build.csx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env dotnet-script 2 | 3 | #r "nuget:SimpleTasks, 0.9.4" 4 | 5 | using SimpleTasks; 6 | using static SimpleTasks.SimpleTask; 7 | 8 | #nullable enable 9 | 10 | string libDir = "src/PropertyChanged.SourceGenerator"; 11 | string testsDir = "src/PropertyChanged.SourceGenerator.UnitTests"; 12 | 13 | string nugetDir = "NuGet"; 14 | 15 | string CommonFlags(string? version, string? configuration) => 16 | $"--configuration={configuration ?? "Release"} -p:VersionPrefix=\"{version ?? "0.0.0"}\""; 17 | 18 | CreateTask("build").Run((string versionOpt, string configurationOpt) => 19 | { 20 | var flags = CommonFlags(versionOpt, configurationOpt); 21 | Command.Run("dotnet", $"build {flags} \"{libDir}\""); 22 | }); 23 | 24 | CreateTask("package").DependsOn("build").Run((string version, string configurationOpt) => 25 | { 26 | var flags = CommonFlags(version, configurationOpt) + $" --no-build --output=\"{nugetDir}\""; 27 | Command.Run("dotnet", $"pack {flags} \"{libDir}\""); 28 | }); 29 | 30 | CreateTask("test").Run(() => 31 | { 32 | Command.Run("dotnet", $"test \"{testsDir}\""); 33 | }); 34 | 35 | CreateTask("coverage").Run(() => 36 | { 37 | Command.Run("dotnet", $"test -p:AltCover=true {testsDir}"); 38 | Command.Run("dotnet", $"reportgenerator -reports:{testsDir}/coverage.xml -targetdir:coverage -assemblyfilters:+PropertyChanged.SourceGenerator"); 39 | }); 40 | 41 | CreateTask("accept-tests").Run(() => 42 | { 43 | var files = Directory.GetFiles(testsDir, "*.received.cs", SearchOption.AllDirectories); 44 | foreach (var file in files) 45 | { 46 | var renamed = System.Text.RegularExpressions.Regex.Replace(file, @"\.received\.cs$", ".verified.cs"); 47 | Console.WriteLine($"{file} -> {renamed}"); 48 | File.Move(file, renamed, true); 49 | } 50 | }); 51 | 52 | return InvokeTask(Args); 53 | -------------------------------------------------------------------------------- /icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canton7/PropertyChanged.SourceGenerator/28d6a67c2b9a2af79d4bc5708366300bca83275c/icon-small.png -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canton7/PropertyChanged.SourceGenerator/28d6a67c2b9a2af79d4bc5708366300bca83275c/icon.png -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.Benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 |  2 | using Basic.Reference.Assemblies; 3 | using BenchmarkDotNet.Attributes; 4 | using BenchmarkDotNet.Configs; 5 | using BenchmarkDotNet.Diagnostics.Windows.Configs; 6 | using BenchmarkDotNet.Running; 7 | using Microsoft.CodeAnalysis; 8 | using Microsoft.CodeAnalysis.CSharp; 9 | using PropertyChanged.SourceGenerator; 10 | using PropertyChanged.SourceGenerator.UnitTests.Framework; 11 | 12 | [EtwProfiler] 13 | public class Benchmarks 14 | { 15 | private (GeneratorDriver driver, Compilation compilation) selfGeneratedCompilation = default; 16 | private (GeneratorDriver driver, Compilation compilation) baseClassCompilation = default; 17 | 18 | [GlobalSetup] 19 | public void SetUp() 20 | { 21 | var generator = new PropertyChangedSourceGenerator(); 22 | 23 | this.selfGeneratedCompilation = Create(SelfGeneratedInput); 24 | this.baseClassCompilation = Create(BaseClassInput); 25 | 26 | (GeneratorDriver driver, Compilation compilation) Create(string input) 27 | { 28 | GeneratorDriver driver = CSharpGeneratorDriver.Create(new[] { generator.AsSourceGenerator() }, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, true)); 29 | 30 | // Run once first with an empty syntax tree, so that FAWMN gets a chance to set itself up, without the cost of that being counted 31 | var inputCompilation = CSharpCompilation.Create("TestCompilation", 32 | new[] { CSharpSyntaxTree.ParseText(input) }, 33 | ReferenceAssemblies.Net50, 34 | new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, 35 | nullableContextOptions: NullableContextOptions.Disable)); 36 | 37 | driver = driver.RunGenerators(inputCompilation); 38 | 39 | return (driver, inputCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(""))); 40 | } 41 | } 42 | 43 | private void Run((GeneratorDriver driver, Compilation compilation) pair) 44 | { 45 | pair.driver.RunGenerators(pair.compilation); 46 | } 47 | 48 | [Benchmark] 49 | public void SelfGenerated() => this.Run(this.selfGeneratedCompilation); 50 | 51 | [Benchmark] 52 | public void BaseClass() => this.Run(this.baseClassCompilation); 53 | 54 | public const string SelfGeneratedInput = """ 55 | using PropertyChanged.SourceGenerator; 56 | public partial class A1 57 | { 58 | [Notify] private string _fooA; 59 | [Notify] private string _barA; 60 | [Notify] private int _bazA; 61 | } 62 | public partial class B1 63 | { 64 | [Notify] private string _fooB; 65 | [Notify] private string _barB; 66 | [Notify] private int _bazB; 67 | 68 | private void OnBazBChanged(int oldValue, int newValue) { } 69 | private void AnyProperyChanged(string propertyName, object oldValue, object newValue) { } 70 | } 71 | public partial class C1 72 | { 73 | [Notify] private string _fooC; 74 | [Notify] private string _barC; 75 | [Notify] private int _bazC; 76 | } 77 | public partial class C2 : C1 78 | { 79 | [Notify] private string _subC1; 80 | [Notify] private int _subC2; 81 | [Notify] private int _subC3; 82 | [Notify] private double _subC4; 83 | [Notify] private decumal _subC5; 84 | [Notify] private A1 _subC6; 85 | } 86 | """; 87 | 88 | private const string BaseClassInput = """ 89 | using PropertyChanged.SourceGenerator; 90 | using System.ComponentModel; 91 | public class PropertyChangedBase : INotifyPropertyChanged 92 | { 93 | public event PropertyChangedEventHandler PropertyChanged; 94 | protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 95 | } 96 | public partial class A1 : PropertyChangedBase 97 | { 98 | [Notify] private string _fooA; 99 | [Notify] private string _barA; 100 | [Notify] private int _bazA; 101 | } 102 | public partial class B1 : PropertyChangedBase 103 | { 104 | [Notify] private string _fooB; 105 | [Notify] private string _barB; 106 | [Notify] private int _bazB; 107 | 108 | private void OnBazBChanged(int oldValue, int newValue) { } 109 | private void AnyProperyChanged(string propertyName, object oldValue, object newValue) { } 110 | } 111 | public partial class C1 : PropertyChangedBase 112 | { 113 | [Notify] private string _fooC; 114 | [Notify] private string _barC; 115 | [Notify] private int _bazC; 116 | } 117 | public partial class C2 : C1 118 | { 119 | [Notify] private string _subC1; 120 | [Notify] private int _subC2; 121 | [Notify] private int _subC3; 122 | [Notify] private double _subC4; 123 | [Notify] private decumal _subC5; 124 | [Notify] private A1 _subC6; 125 | } 126 | """; 127 | } 128 | 129 | public class Program 130 | { 131 | public static void Main() 132 | { 133 | BenchmarkRunner.Run(); 134 | } 135 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.Benchmarks/PropertyChanged.SourceGenerator.Benchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.HandlesGetterAndSetterEquallyAccessible_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | internal string Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.HandlesGetterLessAccessibleThanSetter_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | protected string Foo { private protected get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.HandlesGetterMoreAccessibleThanSetter_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | internal string Foo { get; private set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.HandlesInternalGetterProtectedSetter_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | protected internal string Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.HandlesProtectedGetterInternalSetter_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | protected internal string Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.RaisesIfBackingFieldIsReadOnly_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.RaisesIfBackingPropertyIsGetterOnly_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.RaisesIfBackingPropertyIsSetterOnly_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | using PropertyChanged.SourceGenerator.UnitTests.Framework; 8 | 9 | namespace PropertyChanged.SourceGenerator.UnitTests; 10 | 11 | [TestFixture] 12 | public class AccessibilityTests : TestsBase 13 | { 14 | [Test] 15 | public void HandlesGetterMoreAccessibleThanSetter() 16 | { 17 | string input = """ 18 | public partial class SomeViewModel 19 | { 20 | [Notify(Getter.Internal, Setter.Private)] 21 | private string _foo; 22 | } 23 | """; 24 | 25 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)); 26 | } 27 | 28 | [Test] 29 | public void HandlesGetterLessAccessibleThanSetter() 30 | { 31 | string input = """ 32 | public partial class SomeViewModel 33 | { 34 | [Notify(Getter.PrivateProtected, Setter.Protected)] 35 | private string _foo; 36 | } 37 | """; 38 | 39 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)); 40 | } 41 | 42 | [Test] 43 | public void HandlesGetterAndSetterEquallyAccessible() 44 | { 45 | string input = """ 46 | public partial class SomeViewModel 47 | { 48 | [Notify(Getter.Internal, Setter.Internal)] 49 | private string _foo; 50 | } 51 | """; 52 | 53 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)); 54 | } 55 | 56 | [Test] 57 | public void HandlesInternalGetterProtectedSetter() 58 | { 59 | string input = """ 60 | public partial class SomeViewModel 61 | { 62 | [Notify(Getter.Internal, Setter.Protected)] 63 | private string _foo; 64 | } 65 | """; 66 | 67 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters) 68 | .HasDiagnostics( 69 | // (3,6): Warning INPC005: C# propertes may not have an internal getter and protected setter, or protected setter and internal getter. Defaulting both to protected internal 70 | // Notify(Getter.Internal, Setter.Protected) 71 | Diagnostic("INPC005", @"Notify(Getter.Internal, Setter.Protected)").WithLocation(3, 6) 72 | )); 73 | } 74 | 75 | [Test] 76 | public void HandlesProtectedGetterInternalSetter() 77 | { 78 | string input = """ 79 | public partial class SomeViewModel 80 | { 81 | [Notify(Getter.Internal, Setter.Protected)] 82 | private string _foo; 83 | } 84 | """; 85 | 86 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters) 87 | .HasDiagnostics( 88 | // (3,6): Warning INPC005: C# propertes may not have an internal getter and protected setter, or protected setter and internal getter. Defaulting both to protected internal 89 | // Notify(Getter.Internal, Setter.Protected) 90 | Diagnostic("INPC005", @"Notify(Getter.Internal, Setter.Protected)").WithLocation(3, 6) 91 | )); 92 | } 93 | 94 | [Test] 95 | public void RaisesIfBackingFieldIsReadOnly() 96 | { 97 | string input = """ 98 | public partial class SomeViewModel 99 | { 100 | [Notify] 101 | private readonly int _foo; 102 | } 103 | """; 104 | 105 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters) 106 | .HasDiagnostics( 107 | // (4,26): Warning INPC018: Backing field '_foo' cannot be readonly. Skipping 108 | // _foo 109 | Diagnostic("INPC018", @"_foo").WithLocation(4, 26) 110 | )); 111 | } 112 | 113 | [Test] 114 | public void RaisesIfBackingPropertyIsGetterOnly() 115 | { 116 | string input = """ 117 | public partial class SomeViewModel 118 | { 119 | [Notify] 120 | private int _foo { get; } 121 | } 122 | """; 123 | 124 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters) 125 | .HasDiagnostics( 126 | // (4,17): Warning INPC019: Backing property '_foo' cannot be getter-only. Skipping 127 | // _foo 128 | Diagnostic("INPC019", @"_foo").WithLocation(4, 17) 129 | )); 130 | } 131 | 132 | [Test] 133 | public void RaisesIfBackingPropertyIsSetterOnly() 134 | { 135 | string input = """ 136 | public partial class SomeViewModel 137 | { 138 | [Notify] 139 | private int _foo { set { } } 140 | } 141 | """; 142 | 143 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters) 144 | .HasDiagnostics( 145 | // (4,17): Warning INPC019: Backing property '_foo' must have a getter and a setter. Skipping 146 | // _foo 147 | Diagnostic("INPC019", @"_foo").WithLocation(4, 17) 148 | )); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AdditionalPropertyGenerationTests.AddsVirtual_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public virtual int Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AdditionalPropertyGenerationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | using PropertyChanged.SourceGenerator.UnitTests.Framework; 8 | 9 | namespace PropertyChanged.SourceGenerator.UnitTests; 10 | 11 | public class AdditionalPropertyGenerationTests : TestsBase 12 | { 13 | [Test] 14 | public void AddsVirtual() 15 | { 16 | string input = """ 17 | public partial class SomeViewModel 18 | { 19 | [Notify(IsVirtual = true)] 20 | private int _foo; 21 | } 22 | """; 23 | 24 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.HandlesPathologicalAttributeCases_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Null); 13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Empty); 14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_NonExistent); 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.HandlesStandardNonPropertyNames_PropertyChanged.SourceGenerator.Internal.EventArgsCache.verified.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChanged.SourceGenerator.Internal 2 | { 3 | internal static class EventArgsCache 4 | { 5 | private static global::System.ComponentModel.PropertyChangedEventArgs _PropertyChanged_Null; 6 | public static global::System.ComponentModel.PropertyChangedEventArgs PropertyChanged_Null => _PropertyChanged_Null ??= new global::System.ComponentModel.PropertyChangedEventArgs(null); 7 | private static global::System.ComponentModel.PropertyChangedEventArgs _PropertyChanged_Empty; 8 | public static global::System.ComponentModel.PropertyChangedEventArgs PropertyChanged_Empty => _PropertyChanged_Empty ??= new global::System.ComponentModel.PropertyChangedEventArgs(@""); 9 | private static global::System.ComponentModel.PropertyChangedEventArgs _PropertyChanged_Foo; 10 | public static global::System.ComponentModel.PropertyChangedEventArgs PropertyChanged_Foo => _PropertyChanged_Foo ??= new global::System.ComponentModel.PropertyChangedEventArgs(@"Foo"); 11 | private static global::System.ComponentModel.PropertyChangedEventArgs _PropertyChanged_Item; 12 | public static global::System.ComponentModel.PropertyChangedEventArgs PropertyChanged_Item => _PropertyChanged_Item ??= new global::System.ComponentModel.PropertyChangedEventArgs(@"Item[]"); 13 | } 14 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.HandlesStandardNonPropertyNames_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Null); 13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Empty); 14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Item); 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.NotifiesGeneratedPropertyOnBaseClass_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar 4 | { 5 | get => this._bar; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._bar)) 9 | { 10 | this._bar = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.NotifiesGeneratedProperty_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 13 | } 14 | } 15 | } 16 | public string Bar 17 | { 18 | get => this._bar; 19 | set 20 | { 21 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._bar)) 22 | { 23 | this._bar = value; 24 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.NotifiesPreExistingPropertyOnBaseClass_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar 4 | { 5 | get => this._bar; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._bar)) 9 | { 10 | this._bar = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.NotifiesPreExistingProperty_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.PassesNullForOldAndNewValueIfPropertyDoesNotExist_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | string old_Foo = this._foo; 11 | this._foo = value; 12 | string new_Foo = this._foo; 13 | this.OnPropertyChanged(@"Foo", old_Foo, new_Foo); 14 | this.OnPropertyChanged(@"", (object)null, (object)null); 15 | this.OnPropertyChanged(@"Item[]", (object)null, (object)null); 16 | this.OnPropertyChanged(@"NonExistent", (object)null, (object)null); 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.PassesOldAndNewValue_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | string old_Foo = this._foo; 11 | global::System.Collections.Generic.List old_Bar = this.Bar; 12 | this._foo = value; 13 | string new_Foo = this._foo; 14 | global::System.Collections.Generic.List new_Bar = this.Bar; 15 | this.OnPropertyChanged(@"Foo", old_Foo, new_Foo); 16 | this.OnPropertyChanged(@"Bar", old_Bar, new_Bar); 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.RaisesIfAutoNotifyingSelf_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.RaisesIfPropertyDefinedOnChildClass_Base.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Base 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/DependsOnTests.HandlesDerivedTypeAutoDependsOn_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int Baz { get; set; } 4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 5 | { 6 | base.OnPropertyChanged(eventArgs); 7 | switch (eventArgs.PropertyName) 8 | { 9 | case @"Selected": 10 | { 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Test); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/DependsOnTests.HandlesDerivedTypeManualDependsOn_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int Baz { get; set; } 4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 5 | { 6 | base.OnPropertyChanged(eventArgs); 7 | switch (eventArgs.PropertyName) 8 | { 9 | case @"Selected": 10 | { 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Test); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/DocTests.CopiesDocs_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | /// 4 | /// The Summary 5 | /// 6 | /// 7 | /// The Description. 8 | /// Indented line 9 | /// 10 | public string Foo { get; set; } 11 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/DocTests.GeneratesOnPropertyChangedOrChangingDocNoOldAndNew_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | /// 4 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 5 | /// 6 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging; 7 | public int Foo { get; set; } 8 | /// 9 | /// Raises the PropertyChanged event 10 | /// 11 | /// The EventArgs to use to raise the event 12 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 13 | { 14 | this.PropertyChanged?.Invoke(this, eventArgs); 15 | } 16 | /// 17 | /// Raises the PropertyChanging event 18 | /// 19 | /// The EventArgs to use to raise the event 20 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs) 21 | { 22 | this.PropertyChanging?.Invoke(this, eventArgs); 23 | } 24 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/DocTests.GeneratesOnPropertyChangedOrChangingDocOldAndNew_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | /// 4 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 5 | /// 6 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging; 7 | public int Foo { get; set; } 8 | /// 9 | /// Raises the PropertyChanged event 10 | /// 11 | /// The EventArgs to use to raise the event 12 | /// Current value of the property 13 | /// New value of the property 14 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs, object oldValue, object newValue) 15 | { 16 | this.OnAnyPropertyChanged(eventArgs.PropertyName, oldValue, newValue); 17 | this.PropertyChanged?.Invoke(this, eventArgs); 18 | } 19 | /// 20 | /// Raises the PropertyChanging event 21 | /// 22 | /// The EventArgs to use to raise the event 23 | /// Current value of the property 24 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs, object oldValue) 25 | { 26 | this.OnAnyPropertyChanging(eventArgs.PropertyName, oldValue); 27 | this.PropertyChanging?.Invoke(this, eventArgs); 28 | } 29 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/DocTests.HandlesMalformedXml_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/DocTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.CodeAnalysis.CSharp; 7 | using Microsoft.CodeAnalysis; 8 | using NUnit.Framework; 9 | using PropertyChanged.SourceGenerator.UnitTests.Framework; 10 | 11 | namespace PropertyChanged.SourceGenerator.UnitTests; 12 | 13 | [TestFixture] 14 | public class DocTests : TestsBase 15 | { 16 | private static readonly CSharpSyntaxVisitor[] rewriters = new CSharpSyntaxVisitor[] 17 | { 18 | RemovePropertiesRewriter.Instance, RemoveInpcMembersRewriter.All 19 | }; 20 | 21 | [Test] 22 | public void CopiesDocs() 23 | { 24 | string input = """ 25 | public partial class SomeViewModel 26 | { 27 | /// 28 | /// The Summary 29 | /// 30 | /// 31 | /// The Description. 32 | /// Indented line 33 | /// 34 | [Notify] private string _foo; 35 | } 36 | """; 37 | 38 | this.AssertThat(input, It.HasFile("SomeViewModel", rewriters)); 39 | } 40 | 41 | [Test] 42 | public void HandlesMalformedXml() 43 | { 44 | string input = """ 45 | public partial class SomeViewModel 46 | { 47 | /// 48 | /// Test 49 | /// 50 | [Notify] private string _foo; 51 | } 52 | """; 53 | 54 | this.AssertThat(input, It.HasFile("SomeViewModel", rewriters)); 55 | } 56 | 57 | [Test] 58 | public void GeneratesOnPropertyChangedOrChangingDocNoOldAndNew() 59 | { 60 | string input = """ 61 | using System.ComponentModel; 62 | public partial class SomeViewModel : INotifyPropertyChanging 63 | { 64 | [Notify] private int _foo; 65 | } 66 | """; 67 | 68 | this.AssertThat(input, It.HasFile("SomeViewModel", RemovePropertiesRewriter.Instance)); 69 | } 70 | 71 | [Test] 72 | public void GeneratesOnPropertyChangedOrChangingDocOldAndNew() 73 | { 74 | string input = """ 75 | using System.ComponentModel; 76 | public partial class SomeViewModel : INotifyPropertyChanging 77 | { 78 | [Notify] private int _foo; 79 | private void OnAnyPropertyChanged(string name, object oldValue, object newValue) { } 80 | private void OnAnyPropertyChanging(string name, object oldValue) { } 81 | } 82 | """; 83 | 84 | this.AssertThat(input, It.HasFile("SomeViewModel", RemovePropertiesRewriter.Instance)); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/Framework/DiagnosticResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.CodeAnalysis; 7 | 8 | namespace PropertyChanged.SourceGenerator.UnitTests.Framework; 9 | 10 | public class DiagnosticResult 11 | { 12 | public const DiagnosticSeverity DefaultSeverity = DiagnosticSeverity.Warning; 13 | 14 | public string Code { get; } 15 | public string SquiggledText { get; } 16 | public DiagnosticSeverity Severity { get; set; } = DefaultSeverity; 17 | public List Locations { get; } = new List(); 18 | 19 | public DiagnosticResult(string code, string squiggledText) 20 | { 21 | this.Code = code; 22 | this.SquiggledText = squiggledText; 23 | } 24 | 25 | public DiagnosticResult WithLocation(int line, int column) 26 | { 27 | this.Locations.Add(new DiagnosticResultLocation(line, column)); 28 | return this; 29 | } 30 | 31 | public DiagnosticResult WithSeverity(DiagnosticSeverity severity) 32 | { 33 | this.Severity = severity; 34 | return this; 35 | } 36 | } 37 | 38 | public readonly struct DiagnosticResultLocation 39 | { 40 | public int Line { get; } 41 | public int Column { get; } 42 | 43 | public DiagnosticResultLocation(int line, int column) 44 | { 45 | if (column < -1) 46 | { 47 | throw new ArgumentOutOfRangeException(nameof(column), "column must be >= -1"); 48 | } 49 | 50 | this.Line = line; 51 | this.Column = column; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/Framework/RemoveDocumentationRewriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Microsoft.CodeAnalysis; 8 | using Microsoft.CodeAnalysis.CSharp; 9 | using Microsoft.CodeAnalysis.CSharp.Syntax; 10 | 11 | namespace PropertyChanged.SourceGenerator.UnitTests.Framework; 12 | 13 | public class RemoveDocumentationRewriter : CSharpSyntaxRewriter 14 | { 15 | public static RemoveDocumentationRewriter Instance { get; } = new(); 16 | 17 | public override SyntaxNode? Visit(SyntaxNode? node) => base.Visit(RemoveDocComments(node)); 18 | 19 | private static SyntaxNode? RemoveDocComments(SyntaxNode? node) 20 | { 21 | if (node == null) 22 | { 23 | return node; 24 | } 25 | 26 | // Need to remove the SingleLineDocumentationCommentTrivia, and the WhitespaceTrivia just before it 27 | 28 | var trivia = node.GetLeadingTrivia(); 29 | return node.WithLeadingTrivia(Filter(trivia)); 30 | 31 | static IEnumerable Filter(IEnumerable input) 32 | { 33 | SyntaxTrivia? previous = null; 34 | foreach (var item in input) 35 | { 36 | if (item.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia)) 37 | { 38 | // Don't yield this, and don't yield previous either if it's whitespace 39 | if (previous != null && !previous.Value.IsKind(SyntaxKind.WhitespaceTrivia)) 40 | { 41 | yield return previous.Value; 42 | } 43 | previous = null; 44 | } 45 | else 46 | { 47 | if (previous != null) 48 | { 49 | yield return previous.Value; 50 | } 51 | previous = item; 52 | } 53 | } 54 | 55 | if (previous != null) 56 | { 57 | yield return previous.Value; 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/Framework/RemoveInpcMembersRewriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.CodeAnalysis; 7 | using Microsoft.CodeAnalysis.CSharp; 8 | using Microsoft.CodeAnalysis.CSharp.Syntax; 9 | 10 | namespace PropertyChanged.SourceGenerator.UnitTests.Framework; 11 | 12 | public class RemoveInpcMembersRewriter : CSharpSyntaxRewriter 13 | { 14 | private static readonly Configuration config = new(); 15 | private readonly bool removeChanged; 16 | private readonly bool removeChanging; 17 | 18 | public static RemoveInpcMembersRewriter All { get; } = new(true, true); 19 | public static RemoveInpcMembersRewriter Changed { get; } = new(true, false); 20 | public static RemoveInpcMembersRewriter Changing { get; } = new(false, true); 21 | 22 | private RemoveInpcMembersRewriter(bool removeChanged, bool removeChanging) 23 | { 24 | this.removeChanged = removeChanged; 25 | this.removeChanging = removeChanging; 26 | } 27 | 28 | public override SyntaxNode? VisitMethodDeclaration(MethodDeclarationSyntax node) 29 | { 30 | if (config.RaisePropertyChangedMethodNames.Contains(node.Identifier.ValueText)) 31 | { 32 | return this.removeChanged ? null : node; 33 | } 34 | if (config.RaisePropertyChangingMethodNames.Contains(node.Identifier.ValueText)) 35 | { 36 | return this.removeChanging ? null : node; 37 | } 38 | 39 | return base.VisitMethodDeclaration(node); 40 | } 41 | 42 | public override SyntaxNode? VisitEventFieldDeclaration(EventFieldDeclarationSyntax node) 43 | { 44 | if (this.removeChanged && node.Declaration.Variables.Any(x => x.Identifier.ValueText == "PropertyChanged")) 45 | { 46 | return null; 47 | } 48 | if (this.removeChanging && node.Declaration.Variables.Any(x => x.Identifier.ValueText == "PropertyChanging")) 49 | { 50 | return null; 51 | } 52 | 53 | return base.VisitEventFieldDeclaration(node); 54 | } 55 | 56 | public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node) => 57 | this.RewriteTypeDeclaration((TypeDeclarationSyntax)base.VisitClassDeclaration(node)!); 58 | 59 | public override SyntaxNode? VisitStructDeclaration(StructDeclarationSyntax node) => 60 | this.RewriteTypeDeclaration((TypeDeclarationSyntax)base.VisitStructDeclaration(node)!); 61 | 62 | public override SyntaxNode? VisitRecordDeclaration(RecordDeclarationSyntax node) => 63 | this.RewriteTypeDeclaration((TypeDeclarationSyntax)base.VisitRecordDeclaration(node)!); 64 | 65 | private TypeDeclarationSyntax RewriteTypeDeclaration(TypeDeclarationSyntax node) 66 | { 67 | if (node.BaseList?.Types.Count == 0) 68 | { 69 | // The newline is normally attached to the final item in the BaseList 70 | node = node.ReplaceNode(node.BaseList, node.BaseList.WithTrailingTrivia(SyntaxFactory.Whitespace("\r\n"))); 71 | node = node.RemoveNode(node.BaseList!, SyntaxRemoveOptions.KeepTrailingTrivia)!; 72 | // Remove any trailing space from the class name 73 | node = node.WithIdentifier(node.Identifier.WithTrailingTrivia(default(SyntaxTriviaList))); 74 | if (node.TypeParameterList != null) 75 | { 76 | node = node.WithTypeParameterList(node.TypeParameterList.WithTrailingTrivia(default(SyntaxTriviaList))); 77 | } 78 | } 79 | 80 | return node; 81 | } 82 | 83 | public override SyntaxNode? VisitSimpleBaseType(SimpleBaseTypeSyntax node) 84 | { 85 | if (this.removeChanged && node.Type is QualifiedNameSyntax changedSyntax && changedSyntax.ToString() == "global::System.ComponentModel.INotifyPropertyChanged") 86 | { 87 | return null; 88 | } 89 | if (this.removeChanging && node.Type is QualifiedNameSyntax changingSyntax && changingSyntax.ToString() == "global::System.ComponentModel.INotifyPropertyChanging") 90 | { 91 | return null; 92 | } 93 | 94 | return base.VisitSimpleBaseType(node); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/Framework/RemovePropertiesRewriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.CodeAnalysis; 7 | using Microsoft.CodeAnalysis.CSharp; 8 | using Microsoft.CodeAnalysis.CSharp.Syntax; 9 | using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; 10 | 11 | namespace PropertyChanged.SourceGenerator.UnitTests.Framework; 12 | 13 | public class RemovePropertiesRewriter : CSharpSyntaxRewriter 14 | { 15 | public static RemovePropertiesRewriter Instance { get; } = new(); 16 | 17 | public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node) 18 | { 19 | // Remove the whitespace around the leading { and trailing } 20 | return base.VisitPropertyDeclaration(node 21 | .WithIdentifier( 22 | node.Identifier.WithTrailingTrivia(Whitespace(" "))) 23 | .WithAccessorList( 24 | node.AccessorList! 25 | .WithOpenBraceToken(node.AccessorList.OpenBraceToken.WithoutTrivia()) 26 | .WithCloseBraceToken(node.AccessorList.CloseBraceToken 27 | .WithLeadingTrivia(Whitespace(" ")) 28 | .WithTrailingTrivia(LineFeed)))); 29 | } 30 | 31 | public override SyntaxNode? VisitAccessorDeclaration(AccessorDeclarationSyntax node) 32 | { 33 | return base.VisitAccessorDeclaration(node 34 | .WithBody(null) 35 | .WithExpressionBody(null) 36 | .WithKeyword(node.Keyword.WithTrailingTrivia(null)) 37 | .WithLeadingTrivia(Whitespace(" ")) 38 | .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/IsChangedTests.GeneratesIsChangedOnBaseClass_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | this.IsChanged = true; 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/IsChangedTests.GeneratesIsChangedToGeneratedProperty_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public bool IsChanged 4 | { 5 | get => this._isChanged; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._isChanged)) 9 | { 10 | this._isChanged = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_IsChanged); 12 | } 13 | } 14 | } 15 | public string Foo 16 | { 17 | get => this._foo; 18 | set 19 | { 20 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 21 | { 22 | this._foo = value; 23 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 24 | this.IsChanged = true; 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/IsChangedTests.GeneratesIsChangedToProperty_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | this.IsChanged = true; 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/IsChangedTests.IgnoresIfGeneratedIsChangedPropertyOnBaseClassHasPrivateSetter_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int? Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/IsChangedTests.IgnoresIfIsChangedPropertyOnBaseClassHasPrivateSetter_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int? Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/NullableTypeTests.DoesNotGenerateNullableEventIfInFileNullableContext_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 4 | #nullable enable annotations 5 | public string Foo { get; set; } 6 | #nullable disable 7 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 8 | { 9 | this.PropertyChanged?.Invoke(this, eventArgs); 10 | } 11 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/NullableTypeTests.GeneratesNullableEventIfInCompilationNullableContext_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged 3 | { 4 | public event global::System.ComponentModel.PropertyChangedEventHandler? PropertyChanged; 5 | public string Foo { get; set; } 6 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 7 | { 8 | this.PropertyChanged?.Invoke(this, eventArgs); 9 | } 10 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/NullableTypeTests.GeneratesNullablePropertiesIfInCompilationNullableContext_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | partial class SomeViewModel 3 | { 4 | public string? Nullable 5 | { 6 | get => this._nullable; 7 | set 8 | { 9 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._nullable)) 10 | { 11 | this._nullable = value; 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Nullable); 13 | } 14 | } 15 | } 16 | public string NotNullable 17 | { 18 | get => this._notNullable; 19 | set 20 | { 21 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._notNullable)) 22 | { 23 | this._notNullable = value; 24 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_NotNullable); 25 | } 26 | } 27 | } 28 | #nullable disable 29 | public string Oblivious 30 | { 31 | get => this._oblivious; 32 | set 33 | { 34 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._oblivious)) 35 | { 36 | this._oblivious = value; 37 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Oblivious); 38 | } 39 | } 40 | } 41 | #nullable enable 42 | public int? NullableValue 43 | { 44 | get => this._nullableValue; 45 | set 46 | { 47 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._nullableValue)) 48 | { 49 | this._nullableValue = value; 50 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_NullableValue); 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/NullableTypeTests.GeneratesNullablePropertiesIfInFilenNullableContext_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | #nullable enable annotations 4 | public string? Nullable 5 | { 6 | get => this._nullable; 7 | set 8 | { 9 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._nullable)) 10 | { 11 | this._nullable = value; 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Nullable); 13 | } 14 | } 15 | } 16 | #nullable disable 17 | #nullable enable annotations 18 | public string NotNullable 19 | { 20 | get => this._notNullable; 21 | set 22 | { 23 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._notNullable)) 24 | { 25 | this._notNullable = value; 26 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_NotNullable); 27 | } 28 | } 29 | } 30 | #nullable disable 31 | public string Oblivious 32 | { 33 | get => this._oblivious; 34 | set 35 | { 36 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._oblivious)) 37 | { 38 | this._oblivious = value; 39 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Oblivious); 40 | } 41 | } 42 | } 43 | public int? NullableValue 44 | { 45 | get => this._nullableValue; 46 | set 47 | { 48 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._nullableValue)) 49 | { 50 | this._nullableValue = value; 51 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_NullableValue); 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/NullableTypeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.CodeAnalysis; 7 | using Microsoft.CodeAnalysis.CSharp; 8 | using NUnit.Framework; 9 | using PropertyChanged.SourceGenerator.UnitTests.Framework; 10 | 11 | namespace PropertyChanged.SourceGenerator.UnitTests; 12 | 13 | [TestFixture] 14 | public class NullableTypeTests : TestsBase 15 | { 16 | private static readonly CSharpSyntaxVisitor[] rewriters = new CSharpSyntaxVisitor[] 17 | { 18 | RemovePropertiesRewriter.Instance, RemoveDocumentationRewriter.Instance, 19 | }; 20 | 21 | [Test] 22 | public void GeneratesNullableEventIfInCompilationNullableContext() 23 | { 24 | string input = """ 25 | public partial class SomeViewModel 26 | { 27 | [Notify] 28 | private string _foo = ""; 29 | } 30 | """; 31 | 32 | this.AssertThat( 33 | input, 34 | It.HasFile("SomeViewModel", rewriters), 35 | nullableContextOptions: NullableContextOptions.Enable); 36 | } 37 | 38 | [Test] 39 | public void DoesNotGenerateNullableEventIfInFileNullableContext() 40 | { 41 | string input = """ 42 | #nullable enable 43 | public partial class SomeViewModel 44 | { 45 | [Notify] 46 | private string _foo = ""; 47 | } 48 | """; 49 | 50 | this.AssertThat( 51 | input, 52 | It.HasFile("SomeViewModel", rewriters), 53 | nullableContextOptions: NullableContextOptions.Disable); 54 | ; 55 | } 56 | 57 | [Test] 58 | public void GeneratesNullablePropertiesIfInCompilationNullableContext() 59 | { 60 | string input = """ 61 | public partial class SomeViewModel 62 | { 63 | [Notify] 64 | private string? _nullable; 65 | [Notify] 66 | private string _notNullable = ""; 67 | #nullable disable 68 | [Notify] 69 | private string _oblivious; 70 | #nullable restore 71 | [Notify] 72 | private int? _nullableValue; 73 | } 74 | """; 75 | 76 | this.AssertThat( 77 | input, 78 | It.HasFile("SomeViewModel", RemoveInpcMembersRewriter.All), 79 | nullableContextOptions: NullableContextOptions.Enable); 80 | } 81 | 82 | [Test] 83 | public void GeneratesNullablePropertiesIfInFilenNullableContext() 84 | { 85 | string input = """ 86 | #nullable enable 87 | public partial class SomeViewModel 88 | { 89 | [Notify] 90 | private string? _nullable; 91 | [Notify] 92 | private string _notNullable = ""; 93 | #nullable disable 94 | [Notify] 95 | private string _oblivious; 96 | #nullable restore 97 | [Notify] 98 | private int? _nullableValue; 99 | } 100 | """; 101 | 102 | this.AssertThat( 103 | input, 104 | It.HasFile("SomeViewModel", RemoveInpcMembersRewriter.All), 105 | nullableContextOptions: NullableContextOptions.Disable); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.DoesNotGenerateForOverriddenMethod_Base.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Base : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 4 | public string Foo { get; set; } 5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 6 | { 7 | this.OnAnyPropertyChanged(eventArgs.PropertyName); 8 | this.PropertyChanged?.Invoke(this, eventArgs); 9 | } 10 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.DoesNotGenerateForOverriddenMethod_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesForShadowedMethod_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 5 | { 6 | this.OnAnyPropertyChanged(eventArgs.PropertyName); 7 | base.OnPropertyChanged(eventArgs); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesOldAndNewWhenAlsoGeneratingRaisePropertyChanged_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 4 | public string Foo { get; set; } 5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs, object oldValue, object newValue) 6 | { 7 | this.OnAnyPropertyChanged(eventArgs.PropertyName, oldValue, newValue); 8 | this.PropertyChanged?.Invoke(this, eventArgs); 9 | } 10 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesOldAndNewWhenOverridingOldAndNew_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | public int Foo { get; set; } 4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs, object oldValue, object newValue) 5 | { 6 | this.OnAnyPropertyChanged(eventArgs.PropertyName, oldValue, newValue); 7 | base.OnPropertyChanged(eventArgs, oldValue, newValue); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesOldAndNewWhenOverridingParameterless_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | public int Foo { get; set; } 4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 5 | { 6 | this.OnAnyPropertyChanged(eventArgs.PropertyName, (object)null, (object)null); 7 | base.OnPropertyChanged(eventArgs); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesParameterlessWhenAlsoGeneratingRaisePropertyChanged_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 4 | public string Foo { get; set; } 5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 6 | { 7 | this.OnAnyPropertyChanged(eventArgs.PropertyName); 8 | this.PropertyChanged?.Invoke(this, eventArgs); 9 | } 10 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesParameterlessWhenOverridingOldAndNew_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | public int Foo { get; set; } 4 | protected override void OnPropertyChanged(string propertyName, object oldValue, object newValue) 5 | { 6 | this.OnAnyPropertyChanged(propertyName); 7 | base.OnPropertyChanged(propertyName, oldValue, newValue); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesParameterlessWhenOverridingParameterless_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | public int Foo { get; set; } 4 | protected override void OnPropertyChanged(string propertyName) 5 | { 6 | this.OnAnyPropertyChanged(propertyName); 7 | base.OnPropertyChanged(propertyName); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.DoesNotGenerateForOverriddenMethod_Base.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Base 2 | { 3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging; 4 | public string Foo { get; set; } 5 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs) 6 | { 7 | this.OnAnyPropertyChanging(eventArgs.PropertyName); 8 | this.PropertyChanging?.Invoke(this, eventArgs); 9 | } 10 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.DoesNotGenerateForOverriddenMethod_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesForShadowedMethod_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs) 5 | { 6 | this.OnAnyPropertyChanging(eventArgs.PropertyName); 7 | base.OnPropertyChanging(eventArgs); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesOldWhenAlsoGeneratingRaisePropertyChanging_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging; 4 | public string Foo { get; set; } 5 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs, object oldValue) 6 | { 7 | this.OnAnyPropertyChanging(eventArgs.PropertyName, oldValue); 8 | this.PropertyChanging?.Invoke(this, eventArgs); 9 | } 10 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesOldWhenOverridingOld_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int Foo { get; set; } 4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs, object oldValue) 5 | { 6 | this.OnAnyPropertyChanging(eventArgs.PropertyName, oldValue); 7 | base.OnPropertyChanging(eventArgs, oldValue); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesOldWhenOverridingParameterless_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int Foo { get; set; } 4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs) 5 | { 6 | this.OnAnyPropertyChanging(eventArgs.PropertyName, (object)null); 7 | base.OnPropertyChanging(eventArgs); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesParameterlessWhenAlsoGeneratingRaisePropertyChanging_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging; 4 | public string Foo { get; set; } 5 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs) 6 | { 7 | this.OnAnyPropertyChanging(eventArgs.PropertyName); 8 | this.PropertyChanging?.Invoke(this, eventArgs); 9 | } 10 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesParameterlessWhenOverridingOld_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int Foo { get; set; } 4 | protected override void OnPropertyChanging(string propertyName, object oldValue) 5 | { 6 | this.OnAnyPropertyChanging(propertyName); 7 | base.OnPropertyChanging(propertyName, oldValue); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesParameterlessWhenOverridingParameterless_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int Foo { get; set; } 4 | protected override void OnPropertyChanging(string propertyName) 5 | { 6 | this.OnAnyPropertyChanging(propertyName); 7 | base.OnPropertyChanging(propertyName); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.DoesNotCallInaccessibleAlsoNotifyOnBaseClass_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.DoesNotGenerateAlsoNotifyNonCallable_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.DoesNotGenerateAlsoNotifyWithPropertyOnBaseClassAndMethodOnDerived_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.DoesNotMatchMethodWithDifferingParameterTypes_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GenerateParameterlessRaise_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnFooChanged(); 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesAlsoNotifyCallableParameterless_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | this.OnBarChanged(); 13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesAlsoNotifyCallableParameters_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | int old_Bar = this.Bar; 11 | this._foo = value; 12 | int new_Bar = this.Bar; 13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 14 | this.OnBarChanged(old_Bar, new_Bar); 15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesAlsoNotifyOnBaseClass_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | int old_Bar = this.Bar; 11 | this._foo = value; 12 | int new_Bar = this.Bar; 13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 14 | this.OnBarChanged(old_Bar, new_Bar); 15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesAutoDependsOn_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | int old_Bar = this.Bar; 11 | string old_Baz = this.Baz; 12 | this._foo = value; 13 | int new_Bar = this.Bar; 14 | string new_Baz = this.Baz; 15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 16 | this.OnBarChanged(old_Bar, new_Bar); 17 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 18 | this.OnBazChanged(old_Baz, new_Baz); 19 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Baz); 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesDependsOn_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | int old_Bar = this.Bar; 11 | this._foo = value; 12 | int new_Bar = this.Bar; 13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 14 | this.OnBarChanged(old_Bar, new_Bar); 15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesOldAndNewWithMatchingDataType_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | string old_Foo = this._foo; 11 | this._foo = value; 12 | string new_Foo = this._foo; 13 | this.OnFooChanged(old_Foo, new_Foo); 14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesOldAndNewWithParentDataType_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | string old_Foo = this._foo; 11 | this._foo = value; 12 | string new_Foo = this._foo; 13 | this.OnFooChanged(old_Foo, new_Foo); 14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.DoesNotCallInaccessibleAlsoNotifyOnBaseClass_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo); 11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar); 12 | this._foo = value; 13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.DoesNotGenerateAlsoNotifyWithPropertyOnBaseClassAndMethodOnDerived_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo); 11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar); 12 | this._foo = value; 13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GenerateParameterlessRaise_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this.OnFooChanging(); 11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo); 12 | this._foo = value; 13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesAlsoNotifyCallableParameterless_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo); 11 | this.OnBarChanging(); 12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar); 13 | this._foo = value; 14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesAlsoNotifyCallableParameters_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | int old_Bar = this.Bar; 11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo); 12 | this.OnBarChanging(old_Bar); 13 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar); 14 | this._foo = value; 15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 16 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesAlsoNotifyOnBaseClass_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | int old_Bar = this.Bar; 11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo); 12 | this.OnBarChanging(old_Bar); 13 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar); 14 | this._foo = value; 15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 16 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesAutoDependsOn_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | int old_Bar = this.Bar; 11 | string old_Baz = this.Baz; 12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo); 13 | this.OnBarChanging(old_Bar); 14 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar); 15 | this.OnBazChanging(old_Baz); 16 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Baz); 17 | this._foo = value; 18 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 19 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 20 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Baz); 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesDependsOn_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public int Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | int old_Bar = this.Bar; 11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo); 12 | this.OnBarChanging(old_Bar); 13 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar); 14 | this._foo = value; 15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 16 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesOldAndNewWithParentDataType_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | string old_Foo = this._foo; 11 | this.OnFooChanging(old_Foo); 12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo); 13 | this._foo = value; 14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesOldWithMatchingDataType_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | string old_Foo = this._foo; 11 | this.OnFooChanging(old_Foo); 12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo); 13 | this._foo = value; 14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/PropertyAttributeTests.AddsLeadingAndTrailingBracketsIfOmitted_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | [System.Xml.Serialization.XmlIgnore] 4 | public string Foo { get; set; } 5 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/PropertyAttributeTests.CopiesAttributeOntoGeneratedProperty_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | [System.Xml.Serialization.XmlIgnore] 4 | public string Foo { get; set; } 5 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/PropertyAttributeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Xml.Serialization; 7 | using NUnit.Framework; 8 | using PropertyChanged.SourceGenerator.UnitTests.Framework; 9 | 10 | namespace PropertyChanged.SourceGenerator.UnitTests; 11 | 12 | [TestFixture] 13 | public class PropertyAttributeTests : TestsBase 14 | { 15 | [Test] 16 | public void CopiesAttributeOntoGeneratedProperty() 17 | { 18 | string input = """ 19 | public partial class SomeViewModel 20 | { 21 | [PropertyAttribute("[System.Xml.Serialization.XmlIgnore]")] 22 | [Notify] private string _foo; 23 | } 24 | """; 25 | 26 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)); 27 | } 28 | 29 | [Test] 30 | public void AddsLeadingAndTrailingBracketsIfOmitted() 31 | { 32 | string input = """ 33 | public partial class SomeViewModel 34 | { 35 | [PropertyAttribute("System.Xml.Serialization.XmlIgnore")] 36 | [Notify] private string _foo; 37 | } 38 | """; 39 | 40 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)); 41 | } 42 | 43 | [Test] 44 | public void WarnsIfAttributePlacedOnMemberWithoutNotify() 45 | { 46 | string input = """ 47 | public partial class SomeViewModel 48 | { 49 | [PropertyAttribute("System.Xml.Serialization.XmlIgnore")] 50 | private string _foo; 51 | } 52 | """; 53 | this.AssertThat(input, It.HasDiagnostics( 54 | // (3,6): Warning INPC008: [AlsoNotify] is only valid on members which also have [Notify]. Skipping 55 | // PropertyAttribute("System.Xml.Serialization.XmlIgnore") 56 | Diagnostic("INPC008", @"PropertyAttribute(""System.Xml.Serialization.XmlIgnore"")").WithLocation(3, 6) 57 | )); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/PropertyChanged.SourceGenerator.UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7 5 | 6 | false 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | all 24 | runtime; build; native; contentfiles; analyzers; buildtransitive 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.ReportsCollisionWithExplicitNamesInBaseClass_Base.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Base 2 | { 3 | public int Prop { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.ReportsCollisionWithExplicitNamesInBaseClass_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.ReportsCollisionWithTwoExplicitNames_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.ReportsNameCollisionExplicit_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.ReportsNameCollisionInBaseClass_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.ReportsNameCollisionInferred_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.UsesExplicitNameIfGiven_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Prop { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | using PropertyChanged.SourceGenerator.UnitTests.Framework; 8 | 9 | namespace PropertyChanged.SourceGenerator.UnitTests; 10 | 11 | [TestFixture] 12 | public class PropertyNameTests : TestsBase 13 | { 14 | [Test] 15 | public void UsesExplicitNameIfGiven() 16 | { 17 | string input = """ 18 | public partial class SomeViewModel 19 | { 20 | [Notify("Prop")] 21 | private string _foo; 22 | } 23 | """; 24 | 25 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)); 26 | } 27 | 28 | [Test] 29 | public void ReportsNameCollisionInferred() 30 | { 31 | string input = """ 32 | public partial class SomeViewModel 33 | { 34 | [Notify] 35 | private string _foo; 36 | public string Foo; 37 | } 38 | """; 39 | 40 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters) 41 | .HasDiagnostics( 42 | // (4,20): Warning INPC003: Attempted to generate property 'Foo' for member '_foo', but a member with that name already exists. Skipping this property 43 | // _foo 44 | Diagnostic("INPC003", @"_foo").WithLocation(4, 20) 45 | )); 46 | } 47 | 48 | [Test] 49 | public void ReportsNameCollisionExplicit() 50 | { 51 | string input = """ 52 | public partial class SomeViewModel 53 | { 54 | [Notify("Prop")] 55 | private string _foo; 56 | public string Prop; 57 | } 58 | """; 59 | 60 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters) 61 | .HasDiagnostics( 62 | // (4,20): Warning INPC003: Attempted to generate property 'Prop' for member '_foo', but a member with that name already exists. Skipping this property 63 | // _foo 64 | Diagnostic("INPC003", @"_foo").WithLocation(4, 20) 65 | )); 66 | } 67 | 68 | [Test] 69 | public void ReportsNameCollisionInBaseClass() 70 | { 71 | string input = """ 72 | public partial class SomeViewModelBase 73 | { 74 | public string Prop; 75 | } 76 | public partial class SomeViewModel : SomeViewModelBase 77 | { 78 | [Notify("Prop")] 79 | private string _foo; 80 | } 81 | """; 82 | 83 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters) 84 | .HasDiagnostics( 85 | // (8,20): Warning INPC003: Attempted to generate property 'Prop' for member '_foo', but a member with that name already exists. Skipping this property 86 | // _foo 87 | Diagnostic("INPC003", @"_foo").WithLocation(8, 20) 88 | )); 89 | } 90 | 91 | [Test] 92 | public void ReportsCollisionWithTwoExplicitNames() 93 | { 94 | string input = """ 95 | public partial class SomeViewModel 96 | { 97 | [Notify("Prop")] 98 | private string _foo; 99 | [Notify("Prop")] 100 | private string _bar; 101 | } 102 | """; 103 | 104 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters) 105 | .HasDiagnostics( 106 | // (4,20): Warning INPC004: Member '_foo' will have the same generated property name 'Prop' as member '_bar' 107 | // _foo 108 | Diagnostic("INPC004", @"_foo").WithLocation(4, 20), 109 | 110 | // (6,20): Warning INPC004: Member '_bar' will have the same generated property name 'Prop' as member '_foo' 111 | // _bar 112 | Diagnostic("INPC004", @"_bar").WithLocation(6, 20) 113 | )); 114 | } 115 | 116 | [Test] 117 | public void ReportsCollisionWithExplicitNamesInBaseClass() 118 | { 119 | string input = """ 120 | public partial class Base 121 | { 122 | [Notify("Prop")] 123 | private int _foo; 124 | } 125 | public partial class Derived : Base 126 | { 127 | [Notify("Prop")] 128 | private int _bar; 129 | } 130 | """; 131 | 132 | this.AssertThat(input, It 133 | .HasFile("Base", StandardRewriters) 134 | .HasFile("Derived", StandardRewriters) 135 | .HasDiagnostics( 136 | // (9,17): Warning INPC003: Attempted to generate property 'Prop' for member '_bar', but a member with that name already exists. Skipping this property 137 | // _bar 138 | Diagnostic("INPC003", @"_bar").WithLocation(9, 17) 139 | )); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.DoesNotGenerateInpcInterfaceIfAlreadySpecified_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 4 | public string Foo { get; set; } 5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 6 | { 7 | this.PropertyChanged?.Invoke(this, eventArgs); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.FindsAndCallsMethodWithEventArgsAndOldAndNewValues_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | string old_Foo = this._foo; 11 | this._foo = value; 12 | string new_Foo = this._foo; 13 | this.NotifyPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo, old_Foo, new_Foo); 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.FindsAndCallsMethodWithEventArgs_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.FindsAndCallsMethodWithStringNameAndOldAndNewValues_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | string old_Foo = this._foo; 11 | this._foo = value; 12 | string new_Foo = this._foo; 13 | this.NotifyPropertyChanged(@"Foo", old_Foo, new_Foo); 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.FindsAndCallsMethodWithStringName_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.NotifyPropertyChanged(@"Foo"); 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.FindsExplicitlyImplementedEvent_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.FindsGenericBaseClasses_B.verified.cs: -------------------------------------------------------------------------------- 1 | partial class B 2 | { 3 | public string Bar { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.GeneratesInpcInterfaceIfNotSpecified_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 4 | public string Foo { get; set; } 5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 6 | { 7 | this.PropertyChanged?.Invoke(this, eventArgs); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.SearchesForMethodFromTopOfTypeHierarchy_B.verified.cs: -------------------------------------------------------------------------------- 1 | partial class B 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this._foo = value; 11 | this.NotifyOfPropertyChange(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.SearchesForMethodFromTopOfTypeHierarchy_C.verified.cs: -------------------------------------------------------------------------------- 1 | partial class C 2 | { 3 | public string Bar 4 | { 5 | get => this._bar; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._bar)) 9 | { 10 | this._bar = value; 11 | this.OnPropertyChanged(@"Bar"); 12 | } 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.TakesNameFromPropertyChangingIfPossible_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 4 | public int Foo { get; set; } 5 | protected virtual void NotifyOfPropertyChange(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 6 | { 7 | this.PropertyChanged?.Invoke(this, eventArgs); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CallsOnPropertyNameChangedNoOldAndNew_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs, object oldValue, object newValue) 5 | { 6 | base.OnPropertyChanged(eventArgs, oldValue, newValue); 7 | switch (eventArgs.PropertyName) 8 | { 9 | case @"Foo": 10 | { 11 | string new_Bar = this.Bar; 12 | this.OnBarChanged(); 13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar, (object)null, new_Bar); 14 | } 15 | break; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CallsOnPropertyNameChangedOldAndNew_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs, object oldValue, object newValue) 5 | { 6 | base.OnPropertyChanged(eventArgs, oldValue, newValue); 7 | switch (eventArgs.PropertyName) 8 | { 9 | case @"Foo": 10 | { 11 | string new_Bar = this.Bar; 12 | this.OnBarChanged(default(string), new_Bar); 13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar, (object)null, new_Bar); 14 | } 15 | break; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=internal.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | internal override void OnPropertyChanged(string propertyName) 5 | { 6 | base.OnPropertyChanged(propertyName); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanged(@"Bar"); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=private protected.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | private protected override void OnPropertyChanged(string propertyName) 5 | { 6 | base.OnPropertyChanged(propertyName); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanged(@"Bar"); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected internal.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected internal override void OnPropertyChanged(string propertyName) 5 | { 6 | base.OnPropertyChanged(propertyName); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanged(@"Bar"); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanged(string propertyName) 5 | { 6 | base.OnPropertyChanged(propertyName); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanged(@"Bar"); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=public.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | public override void OnPropertyChanged(string propertyName) 5 | { 6 | base.OnPropertyChanged(propertyName); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanged(@"Bar"); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.DefinesOverrideEventArgsNoOldAndNew_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 5 | { 6 | base.OnPropertyChanged(eventArgs); 7 | switch (eventArgs.PropertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.DefinesOverrideEventArgsOldAndNew_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs, object oldValue, object newValue) 5 | { 6 | base.OnPropertyChanged(eventArgs, oldValue, newValue); 7 | switch (eventArgs.PropertyName) 8 | { 9 | case @"Foo": 10 | { 11 | string new_Bar = this.Bar; 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar, (object)null, new_Bar); 13 | } 14 | break; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.DefinesOverrideStringNoOldAndNew_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanged(string propertyName) 5 | { 6 | base.OnPropertyChanged(propertyName); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanged(@"Bar"); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.DefinesOverrideStringOldAndNew_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanged(string propertyName, object oldValue, object newValue) 5 | { 6 | base.OnPropertyChanged(propertyName, oldValue, newValue); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | string new_Bar = this.Bar; 12 | this.OnPropertyChanged(@"Bar", (object)null, new_Bar); 13 | } 14 | break; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.DefinesVirtual_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 4 | public string Bar { get; set; } 5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 6 | { 7 | this.PropertyChanged?.Invoke(this, eventArgs); 8 | switch (eventArgs.PropertyName) 9 | { 10 | case @"Foo": 11 | { 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 13 | } 14 | break; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.GeneratesEventAndRaisePropertyChangedIfNotDefined_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 4 | public string Foo { get; set; } 5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 6 | { 7 | this.PropertyChanged?.Invoke(this, eventArgs); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.GeneratesRaisePropertyChangedIfNotDefined_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo { get; set; } 4 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 5 | { 6 | this.PropertyChanged?.Invoke(this, eventArgs); 7 | } 8 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.HandlesSealedClass_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged 2 | { 3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 4 | public string Foo { get; set; } 5 | private void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs) 6 | { 7 | this.PropertyChanged?.Invoke(this, eventArgs); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.RaisesIfNonVirtualBaseMethodAndOverrideRequired_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.DoesNotGenerateInpcInterfaceIfAlreadySpecified_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging; 4 | public string Foo { get; set; } 5 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs) 6 | { 7 | this.PropertyChanging?.Invoke(this, eventArgs); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.DoesNotGenerateInpcInterfaceIfNotSpecified_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.FindsAndCallsMethodWithEventArgsAndOldValue_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | string old_Foo = this._foo; 11 | this.NotifyPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo, old_Foo); 12 | this._foo = value; 13 | this.NotifyPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.FindsAndCallsMethodWithEventArgs_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo); 11 | this._foo = value; 12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.FindsAndCallsMethodWithStringNameAndOldValue_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | string old_Foo = this._foo; 11 | this.NotifyPropertyChanging(@"Foo", old_Foo); 12 | this._foo = value; 13 | this.NotifyPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.FindsAndCallsMethodWithStringName_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this.NotifyPropertyChanging(@"Foo"); 11 | this._foo = value; 12 | this.NotifyPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.FindsExplicitlyImplementedEvent_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.FindsGenericBaseClasses_B.verified.cs: -------------------------------------------------------------------------------- 1 | partial class B 2 | { 3 | public string Bar { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.SearchesForMethodFromTopOfTypeHierarchy_B.verified.cs: -------------------------------------------------------------------------------- 1 | partial class B 2 | { 3 | public string Foo 4 | { 5 | get => this._foo; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo)) 9 | { 10 | this.NotifyOfPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo); 11 | this._foo = value; 12 | this.NotifyOfPropertyChange(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.SearchesForMethodFromTopOfTypeHierarchy_C.verified.cs: -------------------------------------------------------------------------------- 1 | partial class C 2 | { 3 | public string Bar 4 | { 5 | get => this._bar; 6 | set 7 | { 8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._bar)) 9 | { 10 | this.OnPropertyChanging(@"Bar"); 11 | this._bar = value; 12 | this.NotifyOfPropertyChange(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.TakesNameFromPropertyChangedIfPossible_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging; 4 | public int Foo { get; set; } 5 | protected virtual void RaisePropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs) 6 | { 7 | this.PropertyChanging?.Invoke(this, eventArgs); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CallsOnPropertyNameChangingNoOld_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs, object oldValue) 5 | { 6 | base.OnPropertyChanging(eventArgs, oldValue); 7 | switch (eventArgs.PropertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnBarChanging(); 12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar, (object)null); 13 | } 14 | break; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CallsOnPropertyNameChangingOld_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs, object oldValue) 5 | { 6 | base.OnPropertyChanging(eventArgs, oldValue); 7 | switch (eventArgs.PropertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnBarChanging(default(string)); 12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar, (object)null); 13 | } 14 | break; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=internal.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | internal override void OnPropertyChanging(string propertyName) 5 | { 6 | base.OnPropertyChanging(propertyName); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanging(@"Bar"); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=private protected.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | private protected override void OnPropertyChanging(string propertyName) 5 | { 6 | base.OnPropertyChanging(propertyName); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanging(@"Bar"); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected internal.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected internal override void OnPropertyChanging(string propertyName) 5 | { 6 | base.OnPropertyChanging(propertyName); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanging(@"Bar"); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanging(string propertyName) 5 | { 6 | base.OnPropertyChanging(propertyName); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanging(@"Bar"); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=public.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | public override void OnPropertyChanging(string propertyName) 5 | { 6 | base.OnPropertyChanging(propertyName); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanging(@"Bar"); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.DefinesOverrideEventArgsNoOld_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs) 5 | { 6 | base.OnPropertyChanging(eventArgs); 7 | switch (eventArgs.PropertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.DefinesOverrideEventArgsOld_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs, object oldValue) 5 | { 6 | base.OnPropertyChanging(eventArgs, oldValue); 7 | switch (eventArgs.PropertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar, (object)null); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.DefinesOverrideStringNoOld_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanging(string propertyName) 5 | { 6 | base.OnPropertyChanging(propertyName); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanging(@"Bar"); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.DefinesOverrideStringOld_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Bar { get; set; } 4 | protected override void OnPropertyChanging(string propertyName, object oldValue) 5 | { 6 | base.OnPropertyChanging(propertyName, oldValue); 7 | switch (propertyName) 8 | { 9 | case @"Foo": 10 | { 11 | this.OnPropertyChanging(@"Bar", (object)null); 12 | } 13 | break; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.DefinesVirtual_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging; 4 | public string Bar { get; set; } 5 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs) 6 | { 7 | this.PropertyChanging?.Invoke(this, eventArgs); 8 | switch (eventArgs.PropertyName) 9 | { 10 | case @"Foo": 11 | { 12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar); 13 | } 14 | break; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.DoesNothingIfTypeDoesNotImplementInterface_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.GeneratesEventAndRaisePropertyChangingIfNotDefined_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging; 4 | public string Foo { get; set; } 5 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs) 6 | { 7 | this.PropertyChanging?.Invoke(this, eventArgs); 8 | } 9 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.GeneratesRaisePropertyChangingIfNotDefined_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo { get; set; } 4 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs) 5 | { 6 | this.PropertyChanging?.Invoke(this, eventArgs); 7 | } 8 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.RaisesIfNonVirtualBaseMethodAndOverrideRequired_Derived.verified.cs: -------------------------------------------------------------------------------- 1 | partial class Derived 2 | { 3 | public string Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.GeneratesNamespace_Test.Foo.SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | namespace Test.Foo 2 | { 3 | partial class SomeViewModel 4 | { 5 | public string Foo { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesBadlyNamedGenericTypes_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel<@class> 2 | { 3 | public string Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesGenericTypesWithConstraints_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial class SomeViewModel 2 | { 3 | public string Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesPartialNestedTypes_A.B.C.verified.cs: -------------------------------------------------------------------------------- 1 | partial class A 2 | { 3 | partial class B 4 | { 5 | partial class C 6 | { 7 | public string Field { get; set; } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesRecord_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial record SomeViewModel 2 | { 3 | public int Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesStruct_SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | partial struct SomeViewModel 2 | { 3 | public int Foo { get; set; } 4 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesTwoClassesSameNameDifferentNamespaces_NS1.SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | namespace NS1 2 | { 3 | partial class SomeViewModel 4 | { 5 | public string A { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesTwoClassesSameNameDifferentNamespaces_NS2.SomeViewModel.verified.cs: -------------------------------------------------------------------------------- 1 | namespace NS2 2 | { 3 | partial class SomeViewModel 4 | { 5 | public string A { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.Vsix/PropertyChanged.SourceGenerator.Vsix.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | net472 7 | PropertyChanged.SourceGenerator.Vsix 8 | PropertyChanged.SourceGenerator.Vsix 9 | 10 | 11 | 12 | false 13 | false 14 | false 15 | false 16 | false 17 | false 18 | Roslyn 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Program 27 | $(DevEnvDir)devenv.exe 28 | /rootsuffix $(VSSDKTargetPlatformRegRootSuffix) 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator.Vsix/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PropertyChanged.SourceGenerator 6 | This is a sample diagnostic extension for the .NET Compiler Platform ("Roslyn"). 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/AlwaysFalseEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Text; 5 | 6 | namespace PropertyChanged.SourceGenerator; 7 | 8 | public class AlwaysFalseEqualityComparer : IEqualityComparer 9 | { 10 | public static AlwaysFalseEqualityComparer Instance { get; } = new(); 11 | 12 | public bool Equals(T x, T y) => false; 13 | public int GetHashCode(T obj) => RuntimeHelpers.GetHashCode(obj); 14 | } 15 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/AlsoNotifyMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Text; 5 | using Microsoft.CodeAnalysis; 6 | 7 | namespace PropertyChanged.SourceGenerator.Analysis; 8 | 9 | public readonly record struct AlsoNotifyMember : IMember, IEquatable 10 | { 11 | public string? Name { get; } 12 | public string? FullyQualifiedTypeName { get; } 13 | [MemberNotNullWhen(true, nameof(FullyQualifiedTypeName))] 14 | public bool IsCallable => this.FullyQualifiedTypeName != null; 15 | 16 | public OnPropertyNameChangedInfo? OnPropertyNameChanged { get; } 17 | public OnPropertyNameChangedInfo? OnPropertyNameChanging { get; } 18 | 19 | private AlsoNotifyMember( 20 | string? name, 21 | ITypeSymbol? type, 22 | OnPropertyNameChangedInfo? onPropertyNameChanged, 23 | OnPropertyNameChangedInfo? onPropertyNameChanging) 24 | { 25 | this.Name = name; 26 | this.FullyQualifiedTypeName = type?.ToDisplayString(SymbolDisplayFormats.FullyQualifiedTypeName); 27 | this.OnPropertyNameChanged = onPropertyNameChanged; 28 | this.OnPropertyNameChanging = onPropertyNameChanging; 29 | } 30 | 31 | public static AlsoNotifyMember NonCallable(string? name) => 32 | new(name, null, null, null); 33 | 34 | public static AlsoNotifyMember FromMemberAnalysis(MemberAnalysisBuilder memberAnalysis) => 35 | new(memberAnalysis.Name, memberAnalysis.Type, memberAnalysis.OnPropertyNameChanged, memberAnalysis.OnPropertyNameChanging); 36 | 37 | public static AlsoNotifyMember FromProperty( 38 | IPropertySymbol property, 39 | (OnPropertyNameChangedInfo? onPropertyNameChanged, OnPropertyNameChangedInfo? onPropertyNameChanging) namedChangedInfo) 40 | { 41 | // If we have an explicitly-implemented property, use e.g. 'Foo' as the name, not ISomeInterface.Foo 42 | return new( 43 | property.ToDisplayString(SymbolDisplayFormats.SymbolName), 44 | property.Type, 45 | namedChangedInfo.onPropertyNameChanged, 46 | namedChangedInfo.onPropertyNameChanging); 47 | } 48 | } 49 | 50 | public class AlsoNotifyMemberNameOnlyComparer : IEqualityComparer 51 | { 52 | public static AlsoNotifyMemberNameOnlyComparer Instance { get; } = new(); 53 | 54 | public bool Equals(AlsoNotifyMember x, AlsoNotifyMember y) => string.Equals(x.Name, y.Name, StringComparison.Ordinal); 55 | public int GetHashCode(AlsoNotifyMember obj) => obj.Name == null ? 0 : StringComparer.Ordinal.GetHashCode(obj.Name); 56 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/AnalyserInput.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Immutable; 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace PropertyChanged.SourceGenerator.Analysis; 7 | 8 | public readonly struct AnalyserInput : IEquatable 9 | { 10 | public INamedTypeSymbol TypeSymbol { get; } 11 | // Member -> Attributes 12 | public Dictionary> Attributes { get; } = new(SymbolEqualityComparer.Default); 13 | 14 | public AnalyserInput(INamedTypeSymbol typeSymbol) 15 | { 16 | this.TypeSymbol = typeSymbol.OriginalDefinition; 17 | } 18 | 19 | public void Update(ISymbol member, ImmutableArray attributes) 20 | { 21 | if (!this.Attributes.TryGetValue(member, out var existingAttributes)) 22 | { 23 | existingAttributes = new(); 24 | this.Attributes.Add(member, existingAttributes); 25 | } 26 | // Avoid the box 27 | foreach (var attribute in attributes) 28 | { 29 | existingAttributes.Add(attribute); 30 | } 31 | } 32 | 33 | public override bool Equals(object obj) => obj is AnalyserInput other && this.Equals(other); 34 | 35 | public bool Equals(AnalyserInput other) 36 | { 37 | return SymbolEqualityComparer.Default.Equals(this.TypeSymbol, other.TypeSymbol); 38 | } 39 | 40 | public override int GetHashCode() 41 | { 42 | return SymbolEqualityComparer.Default.GetHashCode(this.TypeSymbol); 43 | } 44 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/Analyser_AlsoNotify.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.CodeAnalysis; 6 | using static PropertyChanged.SourceGenerator.Analysis.Utils; 7 | 8 | namespace PropertyChanged.SourceGenerator.Analysis; 9 | 10 | public partial class Analyser 11 | { 12 | private void ResolveAlsoNotify(TypeAnalysisBuilder typeAnalysis, List baseTypeAnalyses) 13 | { 14 | // We've already warned if there are AlsoNotify attributes on members that we haven't analysed 15 | foreach (var member in typeAnalysis.Members) 16 | { 17 | var alsoNotifyAttributes = member.Attributes.Where(x => x.AttributeClass?.Name == "AlsoNotifyAttribute"); 18 | foreach (var attribute in alsoNotifyAttributes) 19 | { 20 | var alsoNotifyValues = ExtractAttributeStringParams(attribute); 21 | 22 | // We only allow them to use the property name. If we didn't, consider: 23 | // 1. Derived class has the same property name as base class (shadowed) 24 | // 2. Derived class has the same field name as a base class (generating different properties) 25 | // 3. Derived class has a field with the same name as a base class' property 26 | // 4. Derived class has a property with the same name as a base class' field 27 | 28 | foreach (string? alsoNotify in alsoNotifyValues) 29 | { 30 | // Remember that we're probably, but not necessarily, notifying a property which we're also 31 | // generating. 32 | // Allow null and emptystring as special cases 33 | if (alsoNotify == member.Name) 34 | { 35 | this.diagnostics.ReportAlsoNotifyForSelf(alsoNotify, attribute, member.BackingMember); 36 | } 37 | else 38 | { 39 | AlsoNotifyMember? alsoNotifyMember = null; 40 | if (!string.IsNullOrEmpty(alsoNotify)) 41 | { 42 | bool foundAlsoNotify = false; 43 | if (baseTypeAnalyses.Prepend(typeAnalysis) 44 | .SelectMany(x => x.Members) 45 | .FirstOrDefault(x => x.Name == alsoNotify) 46 | is { } foundMemberAnalysis) 47 | { 48 | foundAlsoNotify = true; 49 | alsoNotifyMember = AlsoNotifyMember.FromMemberAnalysis(foundMemberAnalysis); 50 | } 51 | else if (TypeAndBaseTypes(typeAnalysis.TypeSymbol) 52 | .SelectMany(x => x.GetMembers(alsoNotify!)) 53 | .OfType() 54 | .FirstOrDefault(x => this.compilation.IsSymbolAccessibleWithin(x, typeAnalysis.TypeSymbol)) 55 | is { } foundProperty) 56 | { 57 | foundAlsoNotify = true; 58 | alsoNotifyMember = AlsoNotifyMember.FromProperty( 59 | foundProperty, 60 | this.FindOnPropertyNameChangedMethod(typeAnalysis.TypeSymbol, foundProperty)); 61 | } 62 | else if (alsoNotify!.EndsWith("[]")) 63 | { 64 | string indexerName = alsoNotify.Substring(0, alsoNotify.Length - "[]".Length); 65 | foundAlsoNotify = TypeAndBaseTypes(typeAnalysis.TypeSymbol) 66 | .SelectMany(x => x.GetMembers("this[]")) 67 | .OfType() 68 | .Any(x => x.IsIndexer && x.MetadataName == indexerName); 69 | } 70 | 71 | if (!foundAlsoNotify) 72 | { 73 | this.diagnostics.ReportAlsoNotifyPropertyDoesNotExist(alsoNotify!, attribute, member.BackingMember); 74 | } 75 | } 76 | 77 | alsoNotifyMember ??= AlsoNotifyMember.NonCallable(alsoNotify); 78 | member.AddAlsoNotify(alsoNotifyMember.Value); 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/Analyser_IsChanged.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.CodeAnalysis; 6 | 7 | namespace PropertyChanged.SourceGenerator.Analysis; 8 | 9 | public partial class Analyser 10 | { 11 | public void ResoveInheritedIsChanged(TypeAnalysisBuilder typeAnalysis, List baseTypeAnalyses) 12 | { 13 | // Copy the parent's, if it's accessible 14 | if (baseTypeAnalyses.FirstOrDefault() is { IsChangedSetterIsPrivate: false } directParent) 15 | { 16 | typeAnalysis.IsChangedPropertyName = directParent.IsChangedPropertyName; 17 | typeAnalysis.IsChangedSetterIsPrivate = false; 18 | } 19 | } 20 | 21 | public void ResolveIsChangedMember( 22 | TypeAnalysisBuilder typeAnalysis, 23 | ISymbol member, 24 | IReadOnlyList attributes, 25 | MemberAnalysisBuilder? memberAnalysis) 26 | { 27 | if (attributes.FirstOrDefault(x => x.AttributeClass?.Name == "IsChangedAttribute") is { } attribute && 28 | GetMemberType(member) is { } memberType) 29 | { 30 | // TODO: Think about if a derived class has an IsChanged property which shadows a base class? 31 | // We'll warn that we'll use the base one, but that's not true. 32 | // TODO: Think about overridden IsChanged properties? 33 | if (typeAnalysis.IsChangedPropertyName != null) 34 | { 35 | this.diagnostics.ReportMultipleIsChangedProperties(typeAnalysis.IsChangedPropertyName, attribute, member); 36 | } 37 | else if (memberType.SpecialType != SpecialType.System_Boolean) 38 | { 39 | this.diagnostics.ReportNonBooleanIsChangedProperty(member); 40 | } 41 | else if (memberAnalysis == null && member is IPropertySymbol { SetMethod: null }) 42 | { 43 | this.diagnostics.ReportIsChangedDoesNotHaveSetter(member); 44 | } 45 | else 46 | { 47 | // If it's got [Notify] on it, use the generated property name 48 | if (memberAnalysis != null) 49 | { 50 | typeAnalysis.IsChangedPropertyName = memberAnalysis.Name; 51 | typeAnalysis.IsChangedSetterIsPrivate = memberAnalysis.SetterAccessibility == Accessibility.Private; 52 | } 53 | else 54 | { 55 | typeAnalysis.IsChangedPropertyName = member.Name; 56 | typeAnalysis.IsChangedSetterIsPrivate = member is IPropertySymbol { SetMethod.DeclaredAccessibility: Accessibility.Private }; 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/IMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Text; 5 | using Microsoft.CodeAnalysis; 6 | 7 | namespace PropertyChanged.SourceGenerator.Analysis; 8 | 9 | public interface IMember 10 | { 11 | [MemberNotNullWhen(true, nameof(FullyQualifiedTypeName))] 12 | [MemberNotNullWhen(true, nameof(Name))] 13 | string? FullyQualifiedTypeName { get; } 14 | string? Name { get; } 15 | 16 | OnPropertyNameChangedInfo? OnPropertyNameChanged { get; } 17 | OnPropertyNameChangedInfo? OnPropertyNameChanging { get; } 18 | } 19 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/InterfaceAnalysis.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Cache; 2 | using Microsoft.CodeAnalysis; 3 | 4 | namespace PropertyChanged.SourceGenerator.Analysis; 5 | 6 | public record InterfaceAnalysis 7 | { 8 | public static InterfaceAnalysis Empty { get; } = new() 9 | { 10 | RequiresInterface = false, 11 | RequiresEvent = false, 12 | CanCallRaiseMethod = false, 13 | EventName = "", 14 | EventArgsFullyQualifiedTypeName = "", 15 | RaiseMethodType = default, 16 | RaiseMethodName = null, 17 | RaiseMethodSignature = default, 18 | OnAnyPropertyChangedOrChangingInfo = null, 19 | }; 20 | 21 | /// 22 | /// True if we need to add this interface to the partial class's list 23 | /// 24 | public required bool RequiresInterface { get; init; } 25 | 26 | /// 27 | /// True if we need to add this event to the partial class 28 | /// 29 | public required bool RequiresEvent { get; init; } 30 | 31 | /// 32 | /// True if this method can be called, because either we're generating it, or because it's user-defined and accessible 33 | /// 34 | public required bool CanCallRaiseMethod { get; init; } 35 | 36 | /// 37 | /// The name of the event: PropertyChanged or PropertyChanging 38 | /// 39 | public required string EventName { get; init; } 40 | 41 | /// 42 | /// The fully-qualified type name of the EventArgs: global::System.ComponentModel.PropertyChangedEventArgs etc 43 | /// 44 | public required string EventArgsFullyQualifiedTypeName { get; init; } 45 | 46 | /// 47 | /// The type of method we're generating, or None if we're not generating it 48 | /// 49 | public required RaisePropertyChangedMethodType RaiseMethodType { get; init; } 50 | 51 | /// 52 | /// The name of the method to raise this event 53 | /// 54 | // TODO: Make this immutable 55 | public required string? RaiseMethodName { get; init; } 56 | 57 | /// 58 | /// The signature of the raise method 59 | /// 60 | public required RaisePropertyChangedOrChangingMethodSignature RaiseMethodSignature { get; init; } 61 | 62 | /// 63 | /// The signature of the 'OnAnyPropertyChanged' method, if any 64 | /// 65 | public required OnPropertyNameChangedInfo? OnAnyPropertyChangedOrChangingInfo { get; init; } 66 | } 67 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/MemberAnalysis.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.CodeAnalysis; 4 | 5 | namespace PropertyChanged.SourceGenerator.Analysis; 6 | 7 | public class MemberAnalysisBuilder 8 | { 9 | public ISymbol BackingMember { get; set; } = null!; 10 | public string Name { get; set; } = null!; 11 | public ITypeSymbol Type { get; set; } = null!; 12 | public bool IsVirtual { get; set; } 13 | public IReadOnlyList Attributes { get; set; } = null!; 14 | public NullableContextOptions? NullableContextOverride { get; set; } 15 | public Accessibility GetterAccessibility { get; set; } 16 | public Accessibility SetterAccessibility { get; set; } 17 | public OnPropertyNameChangedInfo? OnPropertyNameChanged { get; set; } 18 | public OnPropertyNameChangedInfo? OnPropertyNameChanging { get; set; } 19 | public List? AttributesForGeneratedProperty { get; set; } 20 | 21 | public string[]? DocComment { get; set; } 22 | 23 | private HashSet? alsoNotify; 24 | 25 | public void AddAlsoNotify(AlsoNotifyMember alsoNotify) 26 | { 27 | this.alsoNotify ??= new HashSet(AlsoNotifyMemberNameOnlyComparer.Instance); 28 | this.alsoNotify.Add(alsoNotify); 29 | } 30 | 31 | public MemberAnalysis Build() 32 | { 33 | return new MemberAnalysis() 34 | { 35 | Name = this.Name, 36 | FullyQualifiedTypeName = this.Type.ToDisplayString(SymbolDisplayFormats.FullyQualifiedTypeName), 37 | BackingMemberSymbolName = this.BackingMember.ToDisplayString(SymbolDisplayFormats.SymbolName), 38 | IsVirtual = this.IsVirtual, 39 | NullableContextOverride = this.NullableContextOverride, 40 | GetterAccessibility = this.GetterAccessibility, 41 | SetterAccessibility = this.SetterAccessibility, 42 | OnPropertyNameChanged = this.OnPropertyNameChanged, 43 | OnPropertyNameChanging = this.OnPropertyNameChanging, 44 | AttributesForGeneratedProperty = this.AttributesForGeneratedProperty == null ? ReadOnlyEquatableList.Empty : new ReadOnlyEquatableList(this.AttributesForGeneratedProperty), 45 | DocComment = this.DocComment == null ? ReadOnlyEquatableList.Empty : new ReadOnlyEquatableList(this.DocComment), 46 | AlsoNotify = this.alsoNotify == null 47 | ? ReadOnlyEquatableList.Empty 48 | : new ReadOnlyEquatableList(this.alsoNotify.OrderBy(x => x.Name).ToList()), 49 | }; 50 | } 51 | } 52 | 53 | public class MemberAnalysis : IMember 54 | { 55 | public required string Name { get; init; } 56 | public required string FullyQualifiedTypeName { get; init; } 57 | public required string BackingMemberSymbolName { get; init; } 58 | public required bool IsVirtual { get; init; } 59 | public required NullableContextOptions? NullableContextOverride { get; init; } 60 | public required Accessibility GetterAccessibility { get; init; } 61 | public required Accessibility SetterAccessibility { get; init; } 62 | public required OnPropertyNameChangedInfo? OnPropertyNameChanged { get; init; } 63 | public required OnPropertyNameChangedInfo? OnPropertyNameChanging { get; init; } 64 | public required ReadOnlyEquatableList AttributesForGeneratedProperty { get; init; } 65 | 66 | public required ReadOnlyEquatableList DocComment { get; init; } 67 | 68 | public required ReadOnlyEquatableList AlsoNotify { get; init; } 69 | } 70 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/OnPropertyNameChangedInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace PropertyChanged.SourceGenerator.Analysis; 6 | 7 | public record struct OnPropertyNameChangedInfo( 8 | string Name, 9 | bool HasOld, 10 | bool HasNew) 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/RaisePropertyChangedMethodType.cs: -------------------------------------------------------------------------------- 1 | namespace PropertyChanged.SourceGenerator.Analysis; 2 | 3 | public enum RaisePropertyChangedMethodType 4 | { 5 | None, 6 | Virtual, 7 | Override, 8 | NonVirtual, 9 | } 10 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/RaisePropertyChangedOrChangingMethodSignature.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using Microsoft.CodeAnalysis; 6 | 7 | namespace PropertyChanged.SourceGenerator.Analysis; 8 | 9 | public enum RaisePropertyChangedOrChangingNameType 10 | { 11 | // The order here is used by RaisePropertyChangedOrChangingMethodSignature.CompareTo! 12 | String = 0, 13 | PropertyChangedEventArgs = 1, 14 | } 15 | 16 | public record struct RaisePropertyChangedOrChangingMethodSignature( 17 | RaisePropertyChangedOrChangingNameType NameType, 18 | bool HasOld, 19 | bool HasNew, 20 | Accessibility Accessibility) 21 | { 22 | } 23 | 24 | // This is a separate class because the comparison doesn't strictly align with RaisePropertyChangedOrChangingMethodSignature's Equals implementation 25 | public class RaisePropertyChangedOrChangingMethodSignatureBetternessComparer : IComparer 26 | { 27 | public static RaisePropertyChangedOrChangingMethodSignatureBetternessComparer Instance { get; } = new(); 28 | 29 | public int Compare(RaisePropertyChangedOrChangingMethodSignature? left, RaisePropertyChangedOrChangingMethodSignature? right) 30 | { 31 | // Not-null is always better than null 32 | if (left == null) 33 | return right == null ? 0 : -1; 34 | if (right == null) 35 | return 1; 36 | 37 | var (x, y) = (left.Value, right.Value); 38 | 39 | // Methods which take PropertyChangedEventArgs are always better than those which don't 40 | int result = Comparer.Default.Compare(x.NameType, y.NameType); 41 | if (result != 0) 42 | { 43 | return result; 44 | } 45 | 46 | // Methods which take old and/or new are better than those that don't 47 | result = (x.HasOld | x.HasNew).CompareTo(y.HasOld | y.HasNew); 48 | if (result != 0) 49 | { 50 | return result; 51 | } 52 | 53 | // We don't care about accessibility when testing equality 54 | return 0; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/TypeAnalysis.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace PropertyChanged.SourceGenerator.Analysis; 7 | 8 | public class TypeAnalysisBuilder 9 | { 10 | public required INamedTypeSymbol TypeSymbol { get; init; } 11 | 12 | public bool CanGenerate { get; set; } 13 | public bool HadException { get; set; } 14 | 15 | public InterfaceAnalysis? INotifyPropertyChanged { get; set; } 16 | public InterfaceAnalysis? INotifyPropertyChanging { get; set; } 17 | 18 | public string? IsChangedPropertyName { get; set; } 19 | public bool IsChangedSetterIsPrivate { get; set; } 20 | public List Members { get; } = new(); 21 | public NullableContextOptions NullableContext { get; set; } 22 | 23 | private HashSet<(string baseProperty, AlsoNotifyMember notifyProperty)>? baseDependsOn; 24 | public void AddDependsOn(string baseProperty, AlsoNotifyMember notifyProperty) 25 | { 26 | this.baseDependsOn ??= new(); 27 | this.baseDependsOn.Add((baseProperty, notifyProperty)); 28 | } 29 | 30 | public TypeAnalysis Build() 31 | { 32 | string? containingNamespace = null; 33 | if (this.TypeSymbol.ContainingNamespace is { IsGlobalNamespace: false } @namespace) 34 | { 35 | containingNamespace = @namespace.ToDisplayString(SymbolDisplayFormats.Namespace); 36 | } 37 | 38 | var outerTypes = new List(); 39 | for (var outerType = this.TypeSymbol.ContainingType; outerType != null; outerType = outerType.ContainingType) 40 | { 41 | outerTypes.Add(outerType.ToDisplayString(SymbolDisplayFormats.TypeDeclaration)); 42 | } 43 | 44 | return new() 45 | { 46 | TypeDeclaration = this.TypeSymbol.ToDisplayString(SymbolDisplayFormats.TypeDeclaration), 47 | TypeNameForGeneratedFileName = this.TypeSymbol.ToDisplayString(SymbolDisplayFormats.GeneratedFileName), 48 | ContainingNamespace = containingNamespace, 49 | OuterTypes = new ReadOnlyEquatableList(outerTypes), 50 | INotifyPropertyChanged = this.INotifyPropertyChanged ?? throw new ArgumentNullException(nameof(this.INotifyPropertyChanged)), 51 | INotifyPropertyChanging = this.INotifyPropertyChanging ?? throw new ArgumentNullException(nameof(this.INotifyPropertyChanging)), 52 | IsChangedPropertyName = this.IsChangedPropertyName, 53 | IsChangedSetterIsPrivate = this.IsChangedSetterIsPrivate, 54 | Members = new ReadOnlyEquatableList(this.Members.Select(x => x.Build()).ToList()), 55 | NullableContext = this.NullableContext, 56 | BaseDependsOn = this.baseDependsOn == null 57 | ? ReadOnlyEquatableList<(string, AlsoNotifyMember)>.Empty 58 | : new ReadOnlyEquatableList<(string, AlsoNotifyMember)>(this.baseDependsOn.OrderBy(x => x.baseProperty).ThenBy(x => x.notifyProperty.Name).ToList()), 59 | }; 60 | } 61 | } 62 | 63 | public record TypeAnalysis 64 | { 65 | public required string TypeDeclaration { get; init; } 66 | public required string TypeNameForGeneratedFileName { get; init; } 67 | public required string? ContainingNamespace { get; init; } 68 | public required ReadOnlyEquatableList OuterTypes { get; init; } 69 | 70 | public required InterfaceAnalysis INotifyPropertyChanged { get; init; } 71 | public required InterfaceAnalysis INotifyPropertyChanging { get; init; } 72 | 73 | public required string? IsChangedPropertyName { get; init; } 74 | public required bool IsChangedSetterIsPrivate { get; init; } 75 | public required ReadOnlyEquatableList Members { get; init; } 76 | public required NullableContextOptions NullableContext { get; init; } 77 | 78 | public required ReadOnlyEquatableList<(string baseProperty, AlsoNotifyMember notifyProperty)> BaseDependsOn { get; init; } 79 | } 80 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/TypeAnalysisLookups.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Text; 6 | using Microsoft.CodeAnalysis; 7 | 8 | namespace PropertyChanged.SourceGenerator.Analysis; 9 | 10 | public class TypeAnalysisLookups 11 | { 12 | private readonly TypeAnalysisBuilder typeAnalysis; 13 | private readonly List baseTypeAnalyses; 14 | 15 | private Dictionary? nameLookup; 16 | private Dictionary? symbolLookup; 17 | 18 | private Dictionary? baseNameLookup; 19 | 20 | 21 | public TypeAnalysisLookups(TypeAnalysisBuilder typeAnalysis, List baseTypeAnalyses) 22 | { 23 | this.typeAnalysis = typeAnalysis; 24 | this.baseTypeAnalyses = baseTypeAnalyses; 25 | } 26 | 27 | public bool TryGetThisType(string name, [NotNullWhen(true)] out MemberAnalysisBuilder? memberAnalysis) 28 | { 29 | this.nameLookup ??= this.typeAnalysis.Members.ToDictionary(x => x.Name, x => x, StringComparer.Ordinal); 30 | return this.nameLookup.TryGetValue(name, out memberAnalysis); 31 | } 32 | 33 | public bool TryGetThisType(ISymbol symbol, [NotNullWhen(true)] out MemberAnalysisBuilder? memberAnalysis) 34 | { 35 | this.symbolLookup ??= this.typeAnalysis.Members.ToDictionary(x => x.BackingMember, x => x, SymbolEqualityComparer.Default); 36 | return this.symbolLookup.TryGetValue(symbol, out memberAnalysis); 37 | } 38 | 39 | public bool TryGetBaseType(string name, [NotNullWhen(true)] out MemberAnalysisBuilder? memberAnalysis) 40 | { 41 | if (this.baseNameLookup == null) 42 | { 43 | this.baseNameLookup = new(StringComparer.Ordinal); 44 | foreach (var baseType in this.baseTypeAnalyses) 45 | { 46 | foreach (var member in baseType.Members) 47 | { 48 | this.baseNameLookup.Add(member.Name, member); 49 | } 50 | } 51 | } 52 | return this.baseNameLookup.TryGetValue(name, out memberAnalysis); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Analysis/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace PropertyChanged.SourceGenerator.Analysis; 7 | public static class Utils 8 | { 9 | public static IEnumerable TypeAndBaseTypes(INamedTypeSymbol type) 10 | { 11 | // Stop at 'object': no point in analysing that 12 | for (var t = type; t != null && t.SpecialType != SpecialType.System_Object; t = t.BaseType) 13 | { 14 | yield return t; 15 | } 16 | } 17 | 18 | public static bool IsAccessibleNormalMethod(IMethodSymbol method, ITypeSymbol typeSymbol, Compilation compilation) => 19 | !method.IsGenericMethod && 20 | method.ReturnsVoid && 21 | compilation.IsSymbolAccessibleWithin(method, typeSymbol); 22 | 23 | public static bool IsNormalParameter(IParameterSymbol parameter) => 24 | parameter.RefKind == RefKind.None; 25 | } 26 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/BetterIndentedTextWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom.Compiler; 3 | using System.IO; 4 | using System.Runtime.CompilerServices; 5 | using System.Text; 6 | using Microsoft.CodeAnalysis.Text; 7 | 8 | namespace PropertyChanged.SourceGenerator; 9 | 10 | public class BetterIndentedTextWriter : IndentedTextWriter 11 | { 12 | public BetterIndentedTextWriter() : base(new StringWriter()) 13 | { 14 | } 15 | 16 | public void Write([InterpolatedStringHandlerArgument("")] ref AppendInterpolatedStringHandler handler) { } 17 | public void WriteLine([InterpolatedStringHandlerArgument("")] ref AppendInterpolatedStringHandler handler) => this.WriteLine(); 18 | 19 | public SourceText ToSourceText() 20 | { 21 | var stringWriter = (StringWriter)this.InnerWriter; 22 | var stringBuilder = stringWriter.GetStringBuilder(); 23 | stringWriter.Close(); // Attempts to interact with it after this point will fail 24 | return new StringBuilderText(stringBuilder); 25 | } 26 | 27 | private class StringBuilderText : SourceText 28 | { 29 | private readonly StringBuilder stringBuilder; 30 | 31 | public StringBuilderText(StringBuilder stringBuilder) 32 | { 33 | this.stringBuilder = stringBuilder; 34 | } 35 | public override Encoding? Encoding => Encoding.UTF8; 36 | 37 | public override char this[int position] => this.stringBuilder[position]; 38 | 39 | public override int Length => this.stringBuilder.Length; 40 | 41 | public override void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) => 42 | this.stringBuilder.CopyTo(sourceIndex, destination, destinationIndex, count); 43 | 44 | public override string ToString(TextSpan span) 45 | { 46 | if (span.End > this.stringBuilder.Length) 47 | { 48 | throw new ArgumentOutOfRangeException(nameof(span)); 49 | } 50 | 51 | return this.stringBuilder.ToString(span.Start, span.Length); 52 | } 53 | } 54 | } 55 | 56 | [InterpolatedStringHandler] 57 | public struct AppendInterpolatedStringHandler 58 | { 59 | private readonly BetterIndentedTextWriter writer; 60 | 61 | public AppendInterpolatedStringHandler(int literalLength, int formattedCount, BetterIndentedTextWriter writer) 62 | { 63 | this.writer = writer; 64 | } 65 | 66 | public void AppendLiteral(string value) => this.writer.Write(value); 67 | public void AppendFormatted(string? value) => this.writer.Write(value); 68 | 69 | public void AppendFormatted(T value) 70 | { 71 | if (value is not null) 72 | { 73 | this.writer.Write(value.ToString()); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Configuration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Immutable; 4 | using System.Text; 5 | 6 | namespace PropertyChanged.SourceGenerator; 7 | 8 | public enum Capitalisation 9 | { 10 | None, 11 | Uppercase, 12 | Lowercase, 13 | } 14 | 15 | public class Configuration 16 | { 17 | public ImmutableArray RaisePropertyChangedMethodNames { get; set; } = ImmutableArray.Create( 18 | "OnPropertyChanged", "RaisePropertyChanged", "NotifyOfPropertyChange", "NotifyPropertyChanged" 19 | ); 20 | 21 | public ImmutableArray RaisePropertyChangingMethodNames { get; set; } = ImmutableArray.Create( 22 | "OnPropertyChanging", "RaisePropertyChanging", "NotifyOfPropertyChanging", "NotifyPropertyChanging" 23 | ); 24 | 25 | public ImmutableArray RemovePrefixes { get; set; } = ImmutableArray.Create("_"); 26 | public ImmutableArray RemoveSuffixes { get; set; } = ImmutableArray.Empty; 27 | public string? AddPrefix { get; set; } = null; 28 | public string? AddSuffix { get; set; } = null; 29 | public Capitalisation FirstLetterCapitalisation { get; set; } = Capitalisation.Uppercase; 30 | public bool EnableAutoNotify { get; set; } = true; 31 | } 32 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/ConfigurationParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Immutable; 4 | using System.Net; 5 | using System.Reflection.Metadata.Ecma335; 6 | using System.Text; 7 | using Microsoft.CodeAnalysis; 8 | using Microsoft.CodeAnalysis.CSharp.Syntax; 9 | using Microsoft.CodeAnalysis.Diagnostics; 10 | 11 | namespace PropertyChanged.SourceGenerator; 12 | 13 | public class ConfigurationParser 14 | { 15 | private readonly AnalyzerConfigOptionsProvider optionsProvider; 16 | 17 | public ConfigurationParser(AnalyzerConfigOptionsProvider optionsProvider) 18 | { 19 | this.optionsProvider = optionsProvider; 20 | } 21 | 22 | public Configuration Parse(SyntaxTree? syntaxTree, DiagnosticReporter diagnostics) 23 | { 24 | var options = syntaxTree == null 25 | ? this.optionsProvider.GlobalOptions 26 | : this.optionsProvider.GetOptions(syntaxTree); 27 | var config = new Configuration(); 28 | 29 | if (options.TryGetValue("propertychanged.onpropertychanged_method_name", out string? changedMethodName)) 30 | { 31 | config.RaisePropertyChangedMethodNames = changedMethodName.Split(';').ToImmutableArray(); 32 | } 33 | if (options.TryGetValue("propertychanged.onpropertychanging_method_name", out string? changingMethodName)) 34 | { 35 | config.RaisePropertyChangingMethodNames = changingMethodName.Split(';').ToImmutableArray(); 36 | } 37 | if (options.TryGetValue("propertychanged.remove_prefixes", out string? removePrefixes)) 38 | { 39 | config.RemovePrefixes = removePrefixes.Split(';').ToImmutableArray(); 40 | } 41 | if (options.TryGetValue("propertychanged.remove_suffixes", out string? removeSuffixes)) 42 | { 43 | config.RemoveSuffixes = removeSuffixes.Split(';').ToImmutableArray(); 44 | } 45 | if (options.TryGetValue("propertychanged.add_prefix", out string? addPrefix)) 46 | { 47 | config.AddPrefix = addPrefix; 48 | } 49 | if (options.TryGetValue("propertychanged.add_suffix", out string? addSuffix)) 50 | { 51 | config.AddSuffix = addSuffix; 52 | } 53 | if (options.TryGetValue("propertychanged.first_letter_capitalization", out string? firstLetterCapitalisation)) 54 | { 55 | if (string.Equals(firstLetterCapitalisation, "none", StringComparison.OrdinalIgnoreCase)) 56 | { 57 | config.FirstLetterCapitalisation = Capitalisation.None; 58 | } 59 | else if (string.Equals(firstLetterCapitalisation, "upper_case", StringComparison.OrdinalIgnoreCase)) 60 | { 61 | config.FirstLetterCapitalisation = Capitalisation.Uppercase; 62 | } 63 | else if (string.Equals(firstLetterCapitalisation, "lower_case", StringComparison.OrdinalIgnoreCase)) 64 | { 65 | config.FirstLetterCapitalisation = Capitalisation.Lowercase; 66 | } 67 | else 68 | { 69 | diagnostics.ReportUnknownFirstLetterCapitalisation(firstLetterCapitalisation); 70 | } 71 | } 72 | config.EnableAutoNotify = this.ParseBool(options, diagnostics, "propertychanged.auto_notify", true); 73 | 74 | return config; 75 | } 76 | 77 | private bool ParseBool(AnalyzerConfigOptions options, DiagnosticReporter diagnostics, string key, bool defaultValue) 78 | { 79 | if (options.TryGetValue(key, out string? value)) 80 | { 81 | if (string.Equals(value, "true", StringComparison.OrdinalIgnoreCase)) 82 | { 83 | return true; 84 | } 85 | if (string.Equals(value, "false", StringComparison.OrdinalIgnoreCase)) 86 | { 87 | return false; 88 | } 89 | 90 | diagnostics.ReportCannotParseConfigBool(key, value); 91 | return defaultValue; 92 | } 93 | else 94 | { 95 | return defaultValue; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/EquatableArray.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Collections.Immutable; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace PropertyChanged.SourceGenerator; 8 | 9 | // Adapted from https://github.com/CommunityToolkit/dotnet/blob/63ba418b6fc86c9c9098ad119bd5e1d82e82ea4b/src/CommunityToolkit.Mvvm.SourceGenerators/Helpers/EquatableArray%7BT%7D.cs 10 | 11 | public static class EquatableArray 12 | { 13 | public static EquatableArray AsEquatableArray(this ImmutableArray array) where T : IEquatable => new(array); 14 | } 15 | 16 | public readonly struct EquatableArray : IEquatable>, IEnumerable 17 | where T : IEquatable 18 | { 19 | private readonly T[]? array; 20 | 21 | public static EquatableArray Empty { get; } = new(ImmutableArray.Empty); 22 | 23 | public EquatableArray(ImmutableArray array) 24 | { 25 | this.array = Unsafe.As, T[]?>(ref array); 26 | } 27 | 28 | public bool Equals(EquatableArray array) 29 | { 30 | return this.AsSpan().SequenceEqual(array.AsSpan()); 31 | } 32 | 33 | public override bool Equals(object obj) => obj is EquatableArray other && this.Equals(other); 34 | 35 | public override int GetHashCode() 36 | { 37 | if (this.array == null) 38 | { 39 | return 0; 40 | } 41 | 42 | int hashCode = -658986029; 43 | 44 | foreach (var item in this.array) 45 | { 46 | hashCode = hashCode * -1521134295 + item.GetHashCode(); 47 | } 48 | 49 | return hashCode; 50 | } 51 | 52 | /// The from the current . 53 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 54 | public ImmutableArray AsImmutableArray() 55 | { 56 | return Unsafe.As>(ref Unsafe.AsRef(in this.array)); 57 | } 58 | 59 | public ReadOnlySpan AsSpan() 60 | { 61 | return this.AsImmutableArray().AsSpan(); 62 | } 63 | 64 | public ImmutableArray.Enumerator GetEnumerator() => this.AsImmutableArray().GetEnumerator(); 65 | 66 | IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.AsImmutableArray()).GetEnumerator(); 67 | 68 | IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.AsImmutableArray()).GetEnumerator(); 69 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/EventArgs/EventArgsCache.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics.Tracing; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.CodeAnalysis; 6 | 7 | namespace PropertyChanged.SourceGenerator.EventArgs; 8 | 9 | public class EventArgsCache 10 | { 11 | private readonly SortedDictionary propertyNameToCacheName; 12 | private int? hashCode; 13 | 14 | public bool IsEmpty => this.propertyNameToCacheName.Count == 0; 15 | 16 | internal EventArgsCache(SortedDictionary propertyNameToCacheName) 17 | { 18 | this.propertyNameToCacheName = propertyNameToCacheName; 19 | } 20 | 21 | public IEnumerable<(string cacheName, string eventArgsTypeName, string? propertyName)> GetEntries() 22 | { 23 | foreach (var kvp in this.propertyNameToCacheName) 24 | { 25 | yield return (kvp.Value, kvp.Key.EventArgsTypeName, kvp.Key.PropertyName); 26 | } 27 | } 28 | 29 | public override bool Equals(object obj) 30 | { 31 | var other = obj as EventArgsCache; 32 | if (ReferenceEquals(this, other)) 33 | return true; 34 | if (other is null) 35 | return false; 36 | 37 | // We cache hash codes, so this is cheap 38 | if (this.GetHashCode() != other.GetHashCode()) 39 | return false; 40 | 41 | if (this.propertyNameToCacheName.Count != other.propertyNameToCacheName.Count) 42 | return false; 43 | 44 | foreach (var (a, b) in this.propertyNameToCacheName.Zip(other.propertyNameToCacheName, (a, b) => (a, b))) 45 | { 46 | if (a.Key != b.Key || a.Value != b.Value) 47 | { 48 | return false; 49 | } 50 | } 51 | 52 | return true; 53 | } 54 | 55 | public override int GetHashCode() 56 | { 57 | if (this.hashCode == null) 58 | { 59 | int hash = 17; 60 | foreach (var kvp in this.propertyNameToCacheName) 61 | { 62 | unchecked 63 | { 64 | hash = hash * 23 + kvp.Key.GetHashCode(); 65 | hash = hash * 23 + kvp.Value.GetHashCode(); 66 | } 67 | } 68 | 69 | this.hashCode = hash; 70 | } 71 | 72 | return this.hashCode.Value; 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/EventArgs/EventArgsCacheBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace PropertyChanged.SourceGenerator.EventArgs; 6 | 7 | public class EventArgsCacheBuilder 8 | { 9 | // propertyName: The string passed to PropertyChangedEventArgs being cached 10 | // cacheName: The name of the generated property on the EventArgs cache 11 | 12 | // Collection of all property names we need to add to the cache 13 | // We use a sorted dictionary to make equality comparisons easier 14 | private readonly SortedDictionary propertyNameToCacheName = new(); 15 | private readonly HashSet cacheNames = new(StringComparer.Ordinal); 16 | 17 | // Collection of just the property names which collided with something else, so we needed to adjust them 18 | private SortedDictionary? collidedPropertyNameToCacheNames; 19 | 20 | public void Add(string? propertyName, string eventName, string eventArgsFullyQualifiedTypeName) 21 | { 22 | var key = new EventArgsCacheKey(propertyName, eventArgsFullyQualifiedTypeName); 23 | if (this.propertyNameToCacheName.ContainsKey(key)) 24 | { 25 | return; 26 | } 27 | 28 | string safeName = EventArgsCacheLookup.Transform(propertyName, eventName); 29 | 30 | string cacheName = safeName; 31 | for (int i = 1; this.cacheNames.Contains(cacheName); i++) 32 | { 33 | cacheName = safeName + i; 34 | } 35 | 36 | if (cacheName != safeName) 37 | { 38 | this.collidedPropertyNameToCacheNames ??= new(); 39 | this.collidedPropertyNameToCacheNames.Add(key, cacheName); 40 | } 41 | 42 | this.propertyNameToCacheName.Add(key, cacheName); 43 | this.cacheNames.Add(cacheName); 44 | } 45 | 46 | public (EventArgsCache cache, EventArgsCacheLookup lookup) ToCacheAndLookup() => (new(this.propertyNameToCacheName), new(this.collidedPropertyNameToCacheNames)); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/EventArgs/EventArgsCacheKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PropertyChanged.SourceGenerator.EventArgs; 4 | 5 | internal readonly struct EventArgsCacheKey : IEquatable, IComparable 6 | { 7 | public string? PropertyName { get; } 8 | public string EventArgsTypeName { get; } 9 | 10 | public EventArgsCacheKey(string? propertyName, string eventArgsTypeName) 11 | { 12 | this.PropertyName = propertyName; 13 | this.EventArgsTypeName = eventArgsTypeName; 14 | } 15 | 16 | public int CompareTo(EventArgsCacheKey other) 17 | { 18 | int result = StringComparer.Ordinal.Compare(this.PropertyName, other.PropertyName); 19 | if (result != 0) 20 | return result; 21 | 22 | result = string.Compare(this.EventArgsTypeName, other.EventArgsTypeName); 23 | return result; 24 | } 25 | 26 | public bool Equals(EventArgsCacheKey other) 27 | { 28 | return string.Equals(this.PropertyName, other.PropertyName, StringComparison.Ordinal) && 29 | this.EventArgsTypeName == other.EventArgsTypeName; 30 | } 31 | 32 | public override int GetHashCode() 33 | { 34 | unchecked 35 | { 36 | int hash = 17; 37 | hash = hash * 23 + (this.PropertyName == null ? 0 : StringComparer.Ordinal.GetHashCode(this.PropertyName)); 38 | hash = hash * 23 + this.EventArgsTypeName.GetHashCode(); 39 | return hash; 40 | } 41 | } 42 | 43 | public override bool Equals(object? obj) => obj is EventArgsCacheKey other && this.Equals(other); 44 | 45 | public static bool operator ==(EventArgsCacheKey left, EventArgsCacheKey right) => left.Equals(right); 46 | public static bool operator !=(EventArgsCacheKey left, EventArgsCacheKey right) => !left.Equals(right); 47 | public static bool operator <(EventArgsCacheKey left, EventArgsCacheKey right) => left.CompareTo(right) < 0; 48 | public static bool operator <=(EventArgsCacheKey left, EventArgsCacheKey right) => left.CompareTo(right) <= 0; 49 | public static bool operator >(EventArgsCacheKey left, EventArgsCacheKey right) => left.CompareTo(right) > 0; 50 | public static bool operator >=(EventArgsCacheKey left, EventArgsCacheKey right) => left.CompareTo(right) >= 0; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/EventArgs/EventArgsCacheLookup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | 8 | namespace PropertyChanged.SourceGenerator.EventArgs; 9 | 10 | public struct EventArgsCacheLookup : IEquatable 11 | { 12 | private static readonly Regex invalidCharsRegex = new(@"(^[^a-zA-Z])|([^a-zA-Z0-9])"); 13 | 14 | 15 | private readonly SortedDictionary? collidedPropertyNameToCacheNames; 16 | 17 | internal EventArgsCacheLookup(SortedDictionary? collidedPropertyNameToCacheNames) 18 | { 19 | this.collidedPropertyNameToCacheNames = collidedPropertyNameToCacheNames; 20 | } 21 | 22 | public string Get(string? propertyName, string eventName, string eventArgsFullyQualifiedTypeName) 23 | { 24 | if (this.collidedPropertyNameToCacheNames != null && 25 | this.collidedPropertyNameToCacheNames.TryGetValue(new EventArgsCacheKey(propertyName, eventArgsFullyQualifiedTypeName), out string cacheName)) 26 | { 27 | return cacheName; 28 | } 29 | 30 | // Else, there were no collisions when using the default transformation 31 | return Transform(propertyName, eventName); 32 | } 33 | 34 | internal static string Transform(string? propertyName, string eventName) 35 | { 36 | return $"{eventName}_" + propertyName switch 37 | { 38 | null => "Null", 39 | "" => "Empty", 40 | "Item[]" => "Item", 41 | // Reserve the names 'Null' and 'Empty' for actual null/empty strings, and 'Item' for indexers 42 | "Null" => "Null1", 43 | "Empty" => "Empty1", 44 | "Item" => "Item1", 45 | _ => invalidCharsRegex.Replace(propertyName, "_"), 46 | }; 47 | } 48 | 49 | public bool Equals(EventArgsCacheLookup other) 50 | { 51 | if (ReferenceEquals(this.collidedPropertyNameToCacheNames, other.collidedPropertyNameToCacheNames)) 52 | return true; 53 | if ((this.collidedPropertyNameToCacheNames == null) != (other.collidedPropertyNameToCacheNames == null)) 54 | return false; 55 | 56 | // If both or either are null, we'd have returned by now 57 | if (this.collidedPropertyNameToCacheNames!.Count != other.collidedPropertyNameToCacheNames!.Count) 58 | return false; 59 | 60 | foreach (var (a, b) in this.collidedPropertyNameToCacheNames.Zip(other.collidedPropertyNameToCacheNames, (a, b) => (a, b))) 61 | { 62 | if (a.Key != b.Key || a.Value != b.Value) 63 | { 64 | return false; 65 | } 66 | } 67 | 68 | return true; 69 | } 70 | 71 | public override bool Equals(object? obj) => obj is EventArgsCacheLookup other && this.Equals(other); 72 | 73 | public override int GetHashCode() 74 | { 75 | if (this.collidedPropertyNameToCacheNames == null) 76 | return 0; 77 | 78 | int hash = 17; 79 | foreach (var kvp in this.collidedPropertyNameToCacheNames) 80 | { 81 | unchecked 82 | { 83 | hash = hash * 23 + kvp.Key.GetHashCode(); 84 | hash = hash * 23 + kvp.Value.GetHashCode(); 85 | } 86 | } 87 | return hash; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/EventArgsCacheGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom.Compiler; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | using Microsoft.CodeAnalysis.Text; 7 | using PropertyChanged.SourceGenerator; 8 | using PropertyChanged.SourceGenerator.EventArgs; 9 | 10 | public class EventArgsCacheGenerator 11 | { 12 | public const string EventArgsCacheName = "EventArgsCache"; 13 | 14 | private readonly BetterIndentedTextWriter writer = new(); 15 | private readonly EventArgsCache eventArgsCache; 16 | 17 | public EventArgsCacheGenerator(EventArgsCache eventArgsCache) 18 | { 19 | this.eventArgsCache = eventArgsCache; 20 | } 21 | 22 | public void GenerateNameCache() 23 | { 24 | this.writer.WriteLine("namespace PropertyChanged.SourceGenerator.Internal"); 25 | this.writer.WriteLine("{"); 26 | this.writer.Indent++; 27 | 28 | this.writer.WriteLine($"internal static class {EventArgsCacheName}"); 29 | this.writer.WriteLine("{"); 30 | this.writer.Indent++; 31 | 32 | var backingFieldNames = new HashSet(); 33 | foreach (var (cacheName, eventArgsTypeName, propertyName) in this.eventArgsCache.GetEntries()) 34 | { 35 | string backingFieldName = "_" + cacheName; 36 | for (int i = 0; !backingFieldNames.Add(backingFieldName); i++) 37 | { 38 | backingFieldName = $"_{cacheName}{i}"; 39 | } 40 | this.writer.WriteLine($"private static {eventArgsTypeName} {backingFieldName};"); 41 | this.writer.WriteLine($"public static {eventArgsTypeName} {cacheName} => " + 42 | $"{backingFieldName} ??= new {eventArgsTypeName}({EscapeString(propertyName)});"); 43 | } 44 | 45 | this.writer.Indent--; 46 | this.writer.WriteLine("}"); 47 | 48 | this.writer.Indent--; 49 | this.writer.WriteLine("}"); 50 | } 51 | 52 | private static string EscapeString(string? str) 53 | { 54 | return str == null 55 | ? "null" 56 | : "@\"" + str.Replace("\"", "\"\"") + "\""; 57 | } 58 | 59 | public SourceText ToSourceText() => this.writer.ToSourceText(); 60 | } 61 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Polyfills/InterpolatedStringHandlerArgumentAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace System.Runtime.CompilerServices; 6 | 7 | /// Indicates which arguments to a method involving an interpolated string handler should be passed to that handler. 8 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] 9 | public sealed class InterpolatedStringHandlerArgumentAttribute : Attribute 10 | { 11 | /// Initializes a new instance of the class. 12 | /// The name of the argument that should be passed to the handler. 13 | /// The empty string may be used as the name of the receiver in an instance method. 14 | public InterpolatedStringHandlerArgumentAttribute(string argument) => Arguments = new string[] { argument }; 15 | 16 | /// Initializes a new instance of the class. 17 | /// The names of the arguments that should be passed to the handler. 18 | /// The empty string may be used as the name of the receiver in an instance method. 19 | public InterpolatedStringHandlerArgumentAttribute(params string[] arguments) => Arguments = arguments; 20 | 21 | /// Gets the names of the arguments that should be passed to the handler. 22 | /// The empty string may be used as the name of the receiver in an instance method. 23 | public string[] Arguments { get; } 24 | } 25 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Polyfills/InterpolatedStringHandlerAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace System.Runtime.CompilerServices; 6 | 7 | /// Indicates the attributed type is to be used as an interpolated string handler. 8 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] 9 | public sealed class InterpolatedStringHandlerAttribute : Attribute 10 | { 11 | /// Initializes the . 12 | public InterpolatedStringHandlerAttribute() { } 13 | } -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Polyfills/NullableAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace System.Diagnostics.CodeAnalysis; 6 | 7 | [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] 8 | internal sealed class NotNullWhenAttribute : Attribute 9 | { 10 | /// Initializes the attribute with the specified return value condition. 11 | /// 12 | /// The return value condition. If the method returns this value, the associated parameter will not be null. 13 | /// 14 | public NotNullWhenAttribute(bool returnValue) => this.ReturnValue = returnValue; 15 | 16 | /// Gets the return value condition. 17 | public bool ReturnValue { get; } 18 | } 19 | 20 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] 21 | internal sealed class MemberNotNullWhenAttribute : Attribute 22 | { 23 | /// Initializes the attribute with the specified return value condition and a field or property member. 24 | /// 25 | /// The return value condition. If the method returns this value, the associated parameter will not be null. 26 | /// 27 | /// 28 | /// The field or property member that is promised to be not-null. 29 | /// 30 | public MemberNotNullWhenAttribute(bool returnValue, string member) 31 | { 32 | this.ReturnValue = returnValue; 33 | this.Members = new[] { member }; 34 | } 35 | 36 | /// Initializes the attribute with the specified return value condition and list of field and property members. 37 | /// 38 | /// The return value condition. If the method returns this value, the associated parameter will not be null. 39 | /// 40 | /// 41 | /// The list of field and property members that are promised to be not-null. 42 | /// 43 | public MemberNotNullWhenAttribute(bool returnValue, params string[] members) 44 | { 45 | this.ReturnValue = returnValue; 46 | this.Members = members; 47 | } 48 | 49 | /// Gets the return value condition. 50 | public bool ReturnValue { get; } 51 | 52 | /// Gets field or property member names. 53 | public string[] Members { get; } 54 | } 55 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Polyfills/RequiredMemberAttribute.cs: -------------------------------------------------------------------------------- 1 | #if !NET7_0_OR_GREATER 2 | namespace System.Runtime.CompilerServices; 3 | #endif 4 | 5 | #if !NET5_0_OR_GREATER 6 | internal static class IsExternalInit { } 7 | #endif 8 | 9 | #if !NET7_0_OR_GREATER 10 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Struct, Inherited = false)] 11 | internal sealed class RequiredMemberAttribute : Attribute { } 12 | 13 | [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] 14 | internal sealed class CompilerFeatureRequiredAttribute : Attribute 15 | { 16 | public CompilerFeatureRequiredAttribute(string featureName) => this.FeatureName = featureName; 17 | 18 | public string FeatureName { get; } 19 | public bool IsOptional { get; init; } 20 | 21 | public const string RefStructs = nameof(RefStructs); 22 | public const string RequiredMembers = nameof(RequiredMembers); 23 | } 24 | #endif 25 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/Polyfills/SetsRequiredMembersAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace System.Diagnostics.CodeAnalysis; 2 | 3 | /// 4 | /// Specifies that this constructor sets all required members for the current type, and callers 5 | /// do not need to set any required members themselves. 6 | /// 7 | [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] 8 | internal sealed class SetsRequiredMembersAttribute : Attribute { } 9 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/PropertyChanged.SourceGenerator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 11.0 6 | enable 7 | false 8 | true 9 | true 10 | 11 | pdbonly 12 | true 13 | 14 | 0.0.0 15 | PropertyChanged.SourceGenerator 16 | INPC;INotifyPropertyChanged;INotifyPropertyChanging;SourceGenerator;MVVM;Binding 17 | Copyright © Antony Male 2021 - 2023 18 | icon.png 19 | README.md 20 | https://github.com/canton7/PropertyChanged.SourceGenerator 21 | MIT 22 | git 23 | https://github.com/canton7/PropertyChanged.SourceGenerator 24 | Antony Male 25 | Powerful INotifyPropertyChanged Source Generator, which generates INPC boilerplate for you as part of your build. 26 | Supports features such as automatic and manual dependencies between properties, notifications when specific properties change, and more. 27 | true 28 | 29 | 30 | 31 | 32 | all 33 | runtime; build; native; contentfiles; analyzers; buildtransitive 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/ReadOnlyEquatableList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | 8 | namespace PropertyChanged.SourceGenerator; 9 | 10 | public readonly struct ReadOnlyEquatableList : IEquatable>, IReadOnlyList 11 | { 12 | private readonly IReadOnlyList inner; 13 | private readonly IEqualityComparer comparer; 14 | 15 | public static ReadOnlyEquatableList Empty { get; } = new ReadOnlyEquatableList(Array.Empty()); 16 | 17 | public ReadOnlyEquatableList(IReadOnlyList inner, IEqualityComparer? comparer = null) 18 | { 19 | this.inner = inner; 20 | this.comparer = comparer ?? EqualityComparer.Default; 21 | } 22 | 23 | public bool Equals(ReadOnlyEquatableList other) 24 | { 25 | return this.inner.SequenceEqual(other.inner, this.comparer); 26 | } 27 | 28 | public override bool Equals(object obj) => obj is ReadOnlyEquatableList other && this.Equals(other); 29 | 30 | public override int GetHashCode() 31 | { 32 | int hashCode = -658986029; 33 | 34 | foreach (var item in this.inner) 35 | { 36 | hashCode = hashCode * -1521134295 + this.comparer.GetHashCode(item); 37 | } 38 | 39 | return hashCode; 40 | } 41 | 42 | public T this[int index] => this.inner[index]; 43 | 44 | public int Count => this.inner.Count; 45 | 46 | public IEnumerator GetEnumerator() => this.inner.GetEnumerator(); 47 | IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.inner).GetEnumerator(); 48 | 49 | // Avoid a box 50 | public IEnumerable Reverse() => this.inner.Reverse(); 51 | } 52 | -------------------------------------------------------------------------------- /src/PropertyChanged.SourceGenerator/SymbolDisplayFormats.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Microsoft.CodeAnalysis; 5 | 6 | namespace PropertyChanged.SourceGenerator; 7 | 8 | public static class SymbolDisplayFormats 9 | { 10 | /// 11 | /// The name of a symbol, suitably escaped. E.g. "List" or "MethodName" 12 | /// 13 | public static SymbolDisplayFormat SymbolName { get; } 14 | 15 | /// 16 | /// The full name of a namespace, suitably escaped. E.g. "System.Collections.Generic" 17 | /// 18 | public static SymbolDisplayFormat Namespace { get; } 19 | 20 | /// 21 | /// A class declaration, but not including accessibility or modifiers, e.g. "class List<T>" 22 | /// 23 | public static SymbolDisplayFormat TypeDeclaration { get; } 24 | 25 | /// 26 | /// A string suitable for use as the return type from a property or method, suitably escaped. 27 | /// E.g. "global::System.Collections.Generic.IEnumerable<T>" 28 | /// 29 | public static SymbolDisplayFormat FullyQualifiedTypeName { get; } 30 | 31 | /// 32 | /// A string suitable for indicating what type an event is defined on, and whether it is implicitly 33 | /// or explicitly implemented 34 | /// 35 | public static SymbolDisplayFormat EventDefinition { get; } 36 | 37 | /// 38 | /// A string suitable for using as part of a generated file name 39 | /// 40 | public static SymbolDisplayFormat GeneratedFileName { get; } 41 | 42 | static SymbolDisplayFormats() 43 | { 44 | SymbolName = new SymbolDisplayFormat( 45 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers); 46 | 47 | Namespace = new SymbolDisplayFormat( 48 | typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, 49 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers); 50 | 51 | TypeDeclaration = new SymbolDisplayFormat( 52 | genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, 53 | kindOptions: SymbolDisplayKindOptions.IncludeTypeKeyword, 54 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers); 55 | 56 | FullyQualifiedTypeName = new SymbolDisplayFormat( 57 | globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included, 58 | typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, 59 | genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, 60 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers 61 | | SymbolDisplayMiscellaneousOptions.UseSpecialTypes 62 | | SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier); 63 | 64 | EventDefinition = new SymbolDisplayFormat( 65 | typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes, 66 | propertyStyle: SymbolDisplayPropertyStyle.NameOnly, 67 | memberOptions: SymbolDisplayMemberOptions.IncludeContainingType 68 | | SymbolDisplayMemberOptions.IncludeExplicitInterface, 69 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers); 70 | 71 | GeneratedFileName = new SymbolDisplayFormat( 72 | typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Sandbox/.editorconfig: -------------------------------------------------------------------------------- 1 | ;[*.cs] 2 | ;propertychanged.add_prefix = Property 3 | ;propertychanged.first_letter_capitalization = lower_case -------------------------------------------------------------------------------- /src/Sandbox/Program.cs: -------------------------------------------------------------------------------- 1 | using PropertyChanged.SourceGenerator; 2 | using System; 3 | using System.ComponentModel; 4 | 5 | namespace Sandbox; 6 | 7 | public class Program 8 | { 9 | public static void Main(string[] args) 10 | { 11 | Console.WriteLine("Hello World!"); 12 | var vm = new Derived(); 13 | } 14 | } 15 | 16 | internal partial class Base 17 | { 18 | [Notify] 19 | private bool _selected; 20 | } 21 | 22 | internal partial class Derived : Base 23 | { 24 | // [DependsOn(nameof(Selected))] 25 | public string Test => Selected ? "foo" : "bar"; 26 | 27 | [Notify] 28 | private int _baz; 29 | } 30 | -------------------------------------------------------------------------------- /src/Sandbox/Sandbox.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------